// // tank // // Import and Playlist Daemon for autoradio project // // // Copyright (C) 2017-2018 Christian Pointner <equinox@helsinki.at> // // This file is part of tank. // // tank is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // tank 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with tank. If not, see <http://www.gnu.org/licenses/>. // package store import ( "fmt" "os" "path/filepath" ) func (st *Store) GetFilePath(group string, id uint64) string { filename := fmt.Sprintf("%d%s", id, st.Audio.Format.Extension()) return filepath.Join(st.getGroupPath(group), filename) } func (st *Store) ListFiles(group string) (files Files, err error) { err = st.db.Where("group_name = ?", group).Find(&files).Error return } func (st *Store) CreateFile(group string, file File) (*File, error) { if _, err := st.CreateGroup(group); err != nil { return nil, err } file.ID = 0 file.GroupName = group err := st.db.Create(&file).Error return &file, err } func (st *Store) GetFile(group string, id uint64) (file *File, err error) { file = &File{} // we have to make sure that the file actually belongs to <group> since permissions are enforced // based on group membership err = st.db.Where("group_name = ?", group).First(file, id).Error return } func (st *Store) UpdateFile(group string, id uint64, file File) (*File, error) { tx := st.db.Begin() // make sure the file exists and actually belongs to <group> since permissions are enforced // based on group membership if err := tx.Where("group_name = ?", group).First(&File{}, id).Error; err != nil { tx.Rollback() return nil, err } file.ID = id file.GroupName = group err := tx.Save(&file).Error tx.Commit() return &file, err } func (st *Store) UpdateFileMetadata(group string, id uint64, metadata map[string]interface{}) (*File, error) { file := &File{ID: id} tx := st.db.Begin() // make sure the file exists and actually belongs to <group> since permissions are enforced // based on group membership if err := tx.Where("group_name = ?", group).First(&file).Error; err != nil { tx.Rollback() return nil, err } if file.Source.Import.State != ImportDone || !file.Source.Import.Success { tx.Rollback() return nil, ErrFileImportNotDone } // TODO: rewrite tags in file md := make(map[string]interface{}) for key, value := range metadata { md["metadata__"+key] = value } if err := tx.Model(&file).Where("group_name = ?", group).Update(md).Error; err != nil { tx.Rollback() return nil, err } tx.Commit() return file, nil } func (st *Store) UpdateFileImportState(group string, id uint64, state ImportState, success bool) (*File, error) { file := &File{ID: id} fields := make(map[string]interface{}) fields["source__import__state"] = state fields["source__import__success"] = success if state == ImportDone && success { if err := st.populateFileMetadataFields(group, id, fields); err != nil { return nil, err } } if err := st.db.Model(&file).Where("group_name = ?", group).Update(fields).Error; err != nil { return nil, err } return file, nil } func (st *Store) UpdateFileSourceHash(group string, id uint64, hash string) (*File, error) { file := &File{ID: id} if err := st.db.Model(&file).Where("group_name = ?", group).Update("source__hash", hash).Error; err != nil { return nil, err } return file, nil } func (st *Store) getFileUsage(id uint64, playlists *Playlists) (err error) { sub := st.db.Model(PlaylistEntry{}).Select("playlist_id").Where("file_id = ?", id).Group("playlist_id").SubQuery() err = st.db.Where("id in ?", sub).Find(playlists).Error return } func (st *Store) GetFileUsage(group string, id uint64) (playlists Playlists, err error) { // make sure the file exists and actually belongs to <group> since permissions are enforced // based on group membership cnt := 0 if err = st.db.Model(&File{ID: id}).Where("group_name = ?", group).Count(&cnt).Error; err != nil { return } if cnt == 0 { return nil, ErrNotFound } err = st.getFileUsage(id, &playlists) return } func (st *Store) DeleteFile(group string, id uint64) (err error) { // make sure the file actually belongs to <group> since permissions are enforced // based on group membership result := st.db.Where("group_name = ?", group).Delete(&File{ID: id}) if err = result.Error; err != nil { // we assume this is due to a FK constraint -> file in use by playlist_entry usageErr := &ErrFileInUse{} if err = st.getFileUsage(id, &(usageErr.Playlists)); err != nil { return } return usageErr } if result.RowsAffected == 0 { return ErrNotFound } filename := st.GetFilePath(group, id) if err := os.Remove(filename); err != nil && !os.IsNotExist(err) { return fmt.Errorf("unable to delete file '%s': %v", filename, err) } return }