// // 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 importer import ( "encoding/json" "errors" "io" "math" "net/url" "sync/atomic" "time" ) const ( DefaultBacklog = 100 SourceSchemeAttachment = "attachment" ) //******* Errors var ( ErrNotImplemented = errors.New("not implemented") ErrNotFound = errors.New("not found") ErrSourceNotSupported = errors.New("source uri format is not supported") ErrImportNotRunning = errors.New("import not running") ErrSourceAlreadyAttached = errors.New("source is already attached") ErrFileNotNew = errors.New("importing already running or done") ErrTooManyJobs = errors.New("too many pending jobs") ErrAlreadyCanceled = errors.New("job is already canceled") ) //******* Source URL type SourceURL url.URL func (s *SourceURL) String() string { return (*url.URL)(s).String() } func (s *SourceURL) MarshalText() (data []byte, err error) { data = []byte(s.String()) return } func (s *SourceURL) UnmarshalText(data []byte) (err error) { _, err = (*url.URL)(s).Parse(string(data)) return } //******* Source type JobSource struct { len uint64 r io.Reader } //******* State type JobState uint32 const ( // it is important that JobCanceled is smaller than any other state of the job // see job.Cancel() for why this is! JobCanceled JobState = iota JobNew JobInitializing JobPending JobRunning JobDestroying ) func (s *JobState) String() string { switch JobState(atomic.LoadUint32((*uint32)(s))) { case JobNew: return "new" case JobInitializing: return "initializing" case JobPending: return "pending" case JobRunning: return "running" case JobDestroying: return "destroying" } return "unknown" } func (s *JobState) fromString(str string) error { switch str { case "new": atomic.StoreUint32((*uint32)(s), uint32(JobNew)) case "initializing": atomic.StoreUint32((*uint32)(s), uint32(JobInitializing)) case "pending": atomic.StoreUint32((*uint32)(s), uint32(JobPending)) case "running": atomic.StoreUint32((*uint32)(s), uint32(JobRunning)) case "destroying": atomic.StoreUint32((*uint32)(s), uint32(JobDestroying)) default: return errors.New("invalid job state: '" + str + "'") } return nil } func (s *JobState) MarshalText() (data []byte, err error) { data = []byte(s.String()) return } func (s *JobState) UnmarshalText(data []byte) (err error) { return s.fromString(string(data)) } //******* Progress type JobProgressStep uint32 const ( StepFetching JobProgressStep = iota StepNormalizing ) func (s JobProgressStep) String() string { switch s { case StepFetching: return "fetching" case StepNormalizing: return "normalizing" } return "unknown" } func (s JobProgressStep) MarshalText() (data []byte, err error) { data = []byte(s.String()) return } type JobProgress uint64 func (p *JobProgress) set(step JobProgressStep, progress float32) { tmp := uint64(step) << 32 tmp |= uint64(math.Float32bits(progress)) atomic.StoreUint64((*uint64)(p), tmp) } func (p *JobProgress) get() (step JobProgressStep, progress float32) { tmp := atomic.LoadUint64((*uint64)(p)) step = JobProgressStep(tmp >> 32) progress = math.Float32frombits(uint32(tmp)) return } func (p *JobProgress) MarshalJSON() ([]byte, error) { step, progress := p.get() return json.Marshal(struct { Step JobProgressStep `json:"step"` Progress float32 `json:"progress"` }{step, progress}) } //******* Timestamp type Timestamp int64 func (t *Timestamp) set(ts time.Time) { atomic.StoreInt64((*int64)(t), ts.Unix()) } func (t *Timestamp) get() (ts time.Time) { return time.Unix(atomic.LoadInt64((*int64)(t)), 0) } func (t *Timestamp) MarshalJSON() (data []byte, err error) { return t.get().MarshalJSON() }