Newer
Older
// tank, Import and Playlist Daemon for Aura project
// Copyright (C) 2017-2020 Christian Pointner <equinox@helsinki.at>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import (
"fmt"
"os"
"path/filepath"
)

Ernesto Rico Schmidt
committed
func (st *Store) GetFilePath(showID uint64, fileID uint64) string {
filename := fmt.Sprintf("%d%s", fileID, st.Audio.Format.Extension())
return filepath.Join(st.getShowPath(showID), filename)

Ernesto Rico Schmidt
committed
func (st *Store) ListFiles(showID uint64, offset, limit int) (files []File, err error) {
err = st.db.Where("show_id = ?", showID).Order("id").Limit(limit).Offset(offset).Find(&files).Error

Ernesto Rico Schmidt
committed
func (st *Store) CreateFile(showID uint64, file File) (*File, error) {
if _, err := st.CreateShow(showID); err != nil {
return nil, err
}
file.ID = 0

Ernesto Rico Schmidt
committed
file.ShowID = showID
err := st.db.Create(&file).Error
return &file, err

Ernesto Rico Schmidt
committed
func (st *Store) GetFile(showID uint64, fileID uint64) (file *File, err error) {
// we have to make sure that the file actually belongs to <show> since permissions are enforced
// based on show membership

Ernesto Rico Schmidt
committed
err = st.db.Where("show_id = ?", showID).First(file, fileID).Error

Ernesto Rico Schmidt
committed
func (st *Store) UpdateFile(showID uint64, id uint64, file File) (out *File, err error) {
defer func() {
if r := recover(); r != nil {
tx.Rollback()
if err == nil {
err = fmt.Errorf("runtime panic: %+v", r)
}
if err = tx.Error; err != nil {
return
// make sure the file exists and actually belongs to <show> since permissions are enforced
// based on show membership

Ernesto Rico Schmidt
committed
if err = tx.Where("show_id = ?", showID).First(&File{}, id).Error; err != nil {

Ernesto Rico Schmidt
committed
file.ShowID = showID
if err = tx.Save(&file).Error; err != nil {
err = tx.Commit().Error
out = &file
return

Ernesto Rico Schmidt
committed
func (st *Store) UpdateFileMetadata(showID uint64, id uint64, metadata map[string]string) (file *File, err error) {
tx := st.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
if err == nil {
err = fmt.Errorf("runtime panic: %+v", r)
}
if err = tx.Error; err != nil {
return
// make sure the file exists and actually belongs to <show> since permissions are enforced
// based on show membership

Ernesto Rico Schmidt
committed
if err = tx.Where("show_id = ?", showID).First(&file).Error; err != nil {
if file.Source.Import.State != ImportDone {
err = ErrFileImportNotDone
return

Ernesto Rico Schmidt
committed
if fields, err = st.metadataFieldsToFile(showID, id, metadata); err != nil {
if err = tx.Model(&file).Where("show_id = ?", showID).Updates(fields).Error; err != nil {

Ernesto Rico Schmidt
committed
func (st *Store) updateFile(showID uint64, id uint64, values ...interface{}) (file *File, err error) {
file = &File{ID: id}
if err = st.db.Model(&file).Where("show_id = ?", showID).Updates(values).Error; err != nil {
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
func (st *Store) updateImportState(showID uint64, id uint64, state ImportState) (file *File, err error) {
file = &File{ID: id}
if err = st.db.Model(&file).Where("show_id = ?", showID).Update("source__import__state", state).Error; err != nil {
return nil, err
}
return file, nil
}
func (st *Store) updateImportError(showID uint64, id uint64, error string) (file *File, err error) {
file = &File{ID: id}
if err = st.db.Model(&file).Where("show_id = ?", showID).Update("source__import__error", error).Error; err != nil {
return nil, err
}
return file, nil
}
func (st *Store) updateHash(showID uint64, id uint64, hash string) (file *File, err error) {
file = &File{ID: id}
if err = st.db.Model(&file).Where("show_id = ?", showID).Update("source__hash", hash).Error; err != nil {
return nil, err
}
return file, nil
}
func (st *Store) updateMetadata(showID, id uint64, metadata map[string]interface{}) (file *File, err error) {
file = &File{ID: id}
// we need to select all the fields with "*" to update them using this function
if err = st.db.Model(&file).Where("show_id = ?", showID).Select("*").Updates(metadata).Error; err != nil {
return nil, err
}
return file, nil
}

Ernesto Rico Schmidt
committed
func (st *Store) SetFileImportStateInitializing(showID uint64, id uint64) (file *File, err error) {
file = &File{ID: id}

Ernesto Rico Schmidt
committed
result := st.db.Model(&file).Where("show_id = ? and source__import__state = ?", showID, ImportNew).Update("source__import__state", ImportInitializing)
if result.Error != nil {
return nil, result.Error
}
if result.RowsAffected != 1 {
return nil, ErrFileNotNew
}
return file, nil
}

Ernesto Rico Schmidt
committed
func (st *Store) SetFileImportStatePending(showID uint64, id uint64) (file *File, err error) {
return st.updateImportState(showID, id, ImportPending)

Ernesto Rico Schmidt
committed
func (st *Store) SetFileImportStateRunning(showID uint64, id uint64) (file *File, err error) {
return st.updateImportState(showID, id, ImportRunning)
func (st *Store) SetFileImportStateDone(showID uint64, id uint64) (file *File, err error) {
if file, err = st.updateImportState(showID, id, ImportDone); err != nil {
return nil, err
}

Ernesto Rico Schmidt
committed
fields, err := st.metadataFieldsFromFile(showID, id)
return st.updateMetadata(showID, id, fields)

Ernesto Rico Schmidt
committed
func (st *Store) SetFileImportStateAborted(showID uint64, id uint64, error string) (file *File, err error) {
if file, err = st.updateImportState(showID, id, ImportAborted); err != nil {
return nil, err
}
return st.updateImportError(showID, id, error)
func (st *Store) UpdateFileSourceHash(showID uint64, id uint64, hash string) (file *File, err error) {
return st.updateHash(showID, id, hash)
func (st *Store) getFileUsage(id uint64, playlists *[]Playlist) (err error) {
sub := st.db.Model(PlaylistEntry{}).Select("playlist_id").Where("file_id = ?", id).Group("playlist_id")
err = st.db.Where("id in (?)", sub).Find(playlists).Error
return
}

Ernesto Rico Schmidt
committed
func (st *Store) GetFileUsage(showID uint64, id uint64) (playlists []Playlist, err error) {
// make sure the file exists and actually belongs to <show> since permissions are enforced
// based on show membership

Ernesto Rico Schmidt
committed
if err = st.db.Model(&File{ID: id}).Where("show_id = ?", showID).Count(&cnt).Error; err != nil {
return
}
if cnt == 0 {
return nil, ErrNotFound
}
return
}

Ernesto Rico Schmidt
committed
func (st *Store) DeleteFile(showID uint64, id uint64) (err error) {
// make sure the file actually belongs to <show> since permissions are enforced
// based on show membership
result := st.db.Where("show_id = ?", showID).Delete(File{ID: id})
// we assume this is due to a FK constraint -> file in use by playlist_entry
if err = st.getFileUsage(id, &usageErr.Playlists); err != nil {
if result.RowsAffected == 0 {
return ErrNotFound
}

Ernesto Rico Schmidt
committed
filename := st.GetFilePath(showID, id)
if err := os.Remove(filename); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("unable to delete file '%s': %v", filename, err)
}
func (st *Store) GetFileShowID(id uint64) (uint64, error) {
// WARNING: using this function subverts the checks performed in the other functions
file := &File{}
if err := st.db.First(file, "id = ?", id).Error; err != nil {