Skip to content
Snippets Groups Projects
files.go 6.28 KiB
Newer Older
Christian Pointner's avatar
Christian Pointner committed
//
//  tank
//
//  Import and Playlist Daemon for autoradio project
//
//
//  Copyright (C) 2017-2019 Christian Pointner <equinox@helsinki.at>
Christian Pointner's avatar
Christian Pointner committed
//
//  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

func (st *Store) GetFilePath(show string, id uint64) string {
	filename := fmt.Sprintf("%d%s", id, st.Audio.Format.Extension())
	return filepath.Join(st.getShowPath(show), filename)
func (st *Store) ListFiles(show string) (files Files, err error) {
	err = st.db.Where("show_name = ?", show).Find(&files).Error
func (st *Store) CreateFile(show string, file File) (*File, error) {
	if _, err := st.CreateShow(show); err != nil {
		return nil, err
	}
	file.ID = 0
	err := st.db.Create(&file).Error
	return &file, err
func (st *Store) GetFile(show string, id 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
	err = st.db.Where("show_name = ?", show).First(file, id).Error
func (st *Store) UpdateFile(show string, id uint64, file File) (*File, error) {
	tx := st.db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			// TODO: make sure to return non-nil err !!!
		}
	}()
	if err := tx.Error; err != nil {
		return nil, err
	}

	// make sure the file exists and actually belongs to <show> since permissions are enforced
	// based on show membership
	if err := tx.Where("show_name = ?", show).First(&File{}, id).Error; err != nil {
		tx.Rollback()
		return nil, err
	}

	err := tx.Save(&file).Error
	if err != nil {
		tx.Rollback()
		return nil, err
	}
	return &file, tx.Commit().Error
func (st *Store) UpdateFileMetadata(show string, id uint64, metadata map[string]string) (*File, error) {
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			// TODO: make sure to return non-nil err !!!
		}
	}()
	if err := tx.Error; err != nil {
		return nil, err
	}

	file := &File{ID: id}
	// make sure the file exists and actually belongs to <show> since permissions are enforced
	// based on show membership
	if err := tx.Where("show_name = ?", show).First(&file).Error; err != nil {
	if file.Source.Import.State != ImportDone {
		tx.Rollback()
		return nil, ErrFileImportNotDone
	}

	fields, err := st.metadataFieldsToFile(show, id, metadata)
	if err != nil {
		tx.Rollback()
		return nil, err
	if err := tx.Model(&file).Where("show_name = ?", show).Update(fields).Error; err != nil {
	return file, tx.Commit().Error
func (st *Store) updateFile(show string, id uint64, values ...interface{}) (file *File, err error) {
	if err = st.db.Model(&file).Where("show_name = ?", show).Update(values...).Error; err != nil {
		return nil, err
func (st *Store) SetFileImportStateInitializing(show string, id uint64) (file *File, err error) {
	file = &File{ID: id}
	result := st.db.Model(&file).Where("show_name = ? and source__import__state = ?", show, ImportNew).Update("source__import__state", ImportInitializing)
	if result.Error != nil {
		return nil, result.Error
	}
	if result.RowsAffected != 1 {
		return nil, ErrFileNotNew
	}

	return file, nil
}

func (st *Store) SetFileImportStatePending(show string, id uint64) (file *File, err error) {
	return st.updateFile(show, id, "source__import__state", ImportPending)
}
func (st *Store) SetFileImportStateRunning(show string, id uint64) (file *File, err error) {
	return st.updateFile(show, id, "source__import__state", ImportRunning)
}

func (st *Store) SetFileImportStateDone(show string, id uint64) (*File, error) {
	fields, err := st.metadataFieldsFromFile(show, id)
	if err != nil {
		return nil, err
	}
	fields["source__import__state"] = ImportDone

	return st.updateFile(show, id, fields)
func (st *Store) SetFileImportStateAborted(show string, id uint64, error string) (file *File, err error) {
	fields := make(map[string]interface{})
	fields["source__import__state"] = ImportAborted
	fields["source__import__error"] = error

	return st.updateFile(show, id, fields)
func (st *Store) UpdateFileSourceHash(show string, id uint64, hash string) (*File, error) {
	return st.updateFile(show, id, "source__hash", hash)
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
func (st *Store) GetFileUsage(show string, id uint64) (playlists Playlists, err error) {
	// make sure the file exists and actually belongs to <show> since permissions are enforced
	// based on show membership
	if err = st.db.Model(&File{ID: id}).Where("show_name = ?", show).Count(&cnt).Error; err != nil {
	err = st.getFileUsage(id, &playlists)
func (st *Store) DeleteFile(show string, 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_name = ?", show).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(show, id)
	if err := os.Remove(filename); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("unable to delete file '%s': %v", filename, err)
	}