<commit_before>package scWriter

import (
	"fmt"
	"log"
	"sync"
	"time"

	ringBuffer "github.com/dh1tw/golang-ring"
	"github.com/dh1tw/gosamplerate"
	"github.com/dh1tw/remoteAudio/audio"
	pa "github.com/gordonklaus/portaudio"
)

// ScWriter implements the audio.Sink interface and is used to write (play)
// audio on a local audio output device (e.g. speakers).
type ScWriter struct {
	sync.RWMutex
	options    Options
	deviceInfo *pa.DeviceInfo
	stream     *pa.Stream
	ring       ringBuffer.Ring
	stash      []float32
	volume     float32
	src        src
}

// src contains a samplerate converter and its needed variables
type src struct {
	gosamplerate.Src
	samplerate float64
	ratio      float64
}

// NewScWriter returns a new soundcard writer for a specific audio output
// device. This is typically a speaker or a pair of headphones.
func NewScWriter(opts ...Option) (*ScWriter, error) {

	if err := pa.Initialize(); err != nil {
		return nil, err
	}

	info, err := pa.DefaultOutputDevice()
	if err != nil {
		return nil, err
	}

	w := &ScWriter{
		options: Options{
			DeviceName:      "default",
			Channels:        2,
			Samplerate:      48000,
			FramesPerBuffer: 480,
			RingBufferSize:  10,
			Latency:         time.Millisecond * 10,
		},
		deviceInfo: info,
		ring:       ringBuffer.Ring{},
		volume:     0.7,
	}

	for _, option := range opts {
		option(&w.options)
	}

	// setup a samplerate converter
	srConv, err := gosamplerate.New(gosamplerate.SRC_SINC_FASTEST, w.options.Channels, 65536)
	if err != nil {
		return nil, fmt.Errorf("player: %v", err)
	}

	w.src = src{
		Src:        srConv,
		samplerate: w.options.Samplerate,
		ratio:      1,
	}

	// select Playback Audio Device
	if w.options.DeviceName != "default" {
		device, err := getPaDevice(w.options.DeviceName)
		if err != nil {
			return nil, err
		}
		w.deviceInfo = device
	}

	// setup Audio Stream
	streamDeviceParam := pa.StreamDeviceParameters{
		Device:   w.deviceInfo,
		Channels: w.options.Channels,
		Latency:  w.options.Latency,
	}

	streamParm := pa.StreamParameters{
		FramesPerBuffer: w.options.FramesPerBuffer,
		Output:          streamDeviceParam,
		SampleRate:      w.options.Samplerate,
	}

	// setup ring buffer
	w.ring.SetCapacity(w.options.RingBufferSize)

	stream, err := pa.OpenStream(streamParm, w.playCb)
	if err != nil {
		return nil,
			fmt.Errorf("unable to open playback audio stream on device %s: %s",
				w.options.DeviceName, err)
	}

	w.stream = stream

	return w, nil
}

// portaudio callback which will be called continously when the stream is
// started; this function should be short and never block
func (p *ScWriter) playCb(in []float32,
	iTime pa.StreamCallbackTimeInfo,
	iFlags pa.StreamCallbackFlags) {
	switch iFlags {
	case pa.OutputUnderflow:
		log.Println("Output Underflow")
		return // move on!
	case pa.OutputOverflow:
		log.Println("Output Overflow")
		return // move on!
	}

	//pull data from Ringbuffer
	p.Lock()
	data := p.ring.Dequeue()
	p.Unlock()

	if data == nil {
		// fill with silence
		// fmt.Println("silence")
		for i := 0; i < len(in); i++ {
			in[i] = 0
		}
		return
	}

	audioData := data.([]float32)

	// should never happen
	if len(audioData) != len(in) {
		log.Printf("unable to play audio frame; expected frame size %d, but got %d",
			len(in), len(audioData))
		return
	}

	//copy data into buffer
	copy(in, audioData)
}

// Start starts streaming audio to the Soundcard output device (e.g. Speaker).
func (p *ScWriter) Start() error {
	if p.stream == nil {
		return fmt.Errorf("portaudio stream not initialized")
	}
	return p.stream.Start()
}

// Stop stops streaming audio.
func (p *ScWriter) Stop() error {
	if p.stream == nil {
		return fmt.Errorf("portaudio stream not initialized")
	}
	return p.stream.Stop()
}

// Close shutsdown properly the soundcard audio device.
func (p *ScWriter) Close() error {
	if p.stream == nil {
		return fmt.Errorf("portaudio stream not initialized")
	}
	p.stream.Abort()
	p.stream.Stop()
	return nil
}

// SetVolume sets the volume for all upcoming audio frames.
func (p *ScWriter) SetVolume(v float32) {
	p.Lock()
	defer p.Unlock()
	if v < 0 {
		p.volume = 0
	} else if v > 1 {
		p.volume = 1
	} else {
		p.volume = v
	}
}

// Volume returns the current volume.
func (p *ScWriter) Volume() float32 {
	p.RLock()
	defer p.RUnlock()
	return p.volume
}

// Write converts the frames in the audio buffer into the right format
// and queues them into a ring buffer for playing on the speaker.
func (p *ScWriter) Write(msg audio.Msg) error {

	var aData []float32
	var err error

	// if necessary adjust the amount of audio channels
	if msg.Channels != p.options.Channels {
		aData = audio.AdjustChannels(msg.Channels, p.options.Channels, msg.Data)
	} else {
		aData = msg.Data
	}

	// if necessary, resample the audio
	if msg.Samplerate != p.options.Samplerate {
		if p.src.samplerate != msg.Samplerate {
			p.src.Reset()
			p.src.samplerate = msg.Samplerate
			p.src.ratio = p.options.Samplerate / msg.Samplerate
		}
		aData, err = p.src.Process(aData, p.src.ratio, false)
		if err != nil {
			return err
		}
	}

	// audio buffer size we want to write into our ring buffer
	// (size expected by portaudio callback)
	expBufferSize := p.options.FramesPerBuffer * p.options.Channels

	// if there is data stashed from previous calles, get it and prepend it
	// to the data received
	if len(p.stash) > 0 {
		aData = append(p.stash, aData...)
		p.stash = p.stash[:0] // empty
	}

	if msg.EOF {
		// get the stuff from the stash
		fmt.Println("EOF!!!")
	}

	// if the audio buffer size is actually smaller than the one we need,
	// then stash it for the next time and return
	if len(aData) < expBufferSize {
		p.stash = aData
		return nil
	}

	// slice of audio buffers which will be enqueued into the ring buffer
	var bData [][]float32

	// if the aData contains multiples of the expected buffer size,
	// then we chop it into (several) buffers
	if len(aData) >= expBufferSize {
		p.Lock()
		vol := p.volume
		p.Unlock()

		for len(aData) >= expBufferSize {
			if vol != 1 {
				// if necessary, adjust the volume
				audio.AdjustVolume(vol, aData[:expBufferSize])
			}
			bData = append(bData, aData[:expBufferSize])
			aData = aData[expBufferSize:]
		}
	}

	// stash the left over
	if len(aData) > 0 {
		p.stash = aData
	}

	p.enqueue(bData, msg.EOF)

	return nil
}

func (p *ScWriter) enqueue(bData [][]float32, EOF bool) {
	p.Lock()
	defer p.Unlock()
	for _, frame := range bData {
		p.ring.Enqueue(frame)
	}
}

// Flush clears all internal buffers
func (p *ScWriter) Flush() {
	p.Lock()
	defer p.Unlock()

	// delete the stash
	p.stash = []float32{}

	p.ring = ringBuffer.Ring{}
	p.ring.SetCapacity(p.options.RingBufferSize)
}

// getPaDevice checks if the Audio Devices actually exist and
// then returns it
func getPaDevice(name string) (*pa.DeviceInfo, error) {
	devices, _ := pa.Devices()
	for _, device := range devices {
		if device.Name == name {
			return device, nil
		}
	}
	return nil, fmt.Errorf("unknown audio device %s", name)
}

// Write converts the frames in the audio buffer into the right format
// and queues them into a ring buffer for playing on the speaker. The token is
// used to indicate if the calling application has to wait before it can
// enqueue the next buffer.
// func (p *ScWriter) Write(msg audio.Msg, token audio.Token) error {

// 	var aData []float32
// 	var err error

// 	// if necessary adjust the amount of audio channels
// 	if msg.Channels != p.options.Channels {
// 		aData = audio.AdjustChannels(msg.Channels, p.options.Channels, msg.Data)
// 	} else {
// 		aData = msg.Data
// 	}

// 	// if necessary, resample the audio
// 	if msg.Samplerate != p.options.Samplerate {
// 		if p.src.samplerate != msg.Samplerate {
// 			p.src.Reset()
// 			p.src.samplerate = msg.Samplerate
// 			p.src.ratio = p.options.Samplerate / msg.Samplerate
// 		}
// 		aData, err = p.src.Process(aData, p.src.ratio, false)
// 		if err != nil {
// 			return err
// 		}
// 	}

// 	// audio buffer size we want to write into our ring buffer
// 	// (size expected by portaudio callback)
// 	expBufferSize := p.options.FramesPerBuffer * p.options.Channels

// 	// if there is data stashed from previous calles, get it and prepend it
// 	// to the data received
// 	if len(p.stash) > 0 {
// 		aData = append(p.stash, aData...)
// 		p.stash = p.stash[:0] // empty
// 	}

// 	if msg.EOF {
// 		// get the stuff from the stash
// 		fmt.Println("EOF!!!")
// 		fmt.Println("stash size:", len(p.stash))
// 	}

// 	// if the audio buffer size is actually smaller than the one we need,
// 	// then stash it for the next time and return
// 	if len(aData) < expBufferSize {
// 		p.stash = aData
// 		return nil
// 	}

// 	// slice of audio buffers which will be enqueued into the ring buffer
// 	var bData [][]float32

// 	p.Lock()
// 	bufCap := p.ring.Capacity()
// 	bufAvail := bufCap - p.ring.Length()
// 	p.Unlock()

// 	// if the aData contains multiples of the expected buffer size,
// 	// then we chop it into (several) buffers
// 	if len(aData) >= expBufferSize {
// 		p.Lock()
// 		vol := p.volume
// 		p.Unlock()

// 		for len(aData) >= expBufferSize {
// 			if vol != 1 {
// 				// if necessary, adjust the volume
// 				audio.AdjustVolume(vol, aData[:expBufferSize])
// 			}
// 			bData = append(bData, aData[:expBufferSize])
// 			aData = aData[expBufferSize:]
// 		}
// 	}

// 	// stash the left over
// 	if len(aData) > 0 {
// 		p.stash = aData
// 	}

// 	// if the msg originates from a stream, we ignore the next statement
// 	// and move on (which could mean that we overwrite data in the
// 	// ring buffer - but thats OK to keep latency low)

// 	// in case we don't have a stream (e.g. writing from a file) and the
// 	// ring buffer might be full, we have to wait until there is some
// 	// space available again in the ring buffer
// 	if !msg.IsStream && bufAvail <= len(bData) {

// 		token.Add(1)

// 		go func() {
// 			for len(bData) > 0 {

// 				// wait until there is enough space in the ring buffer,
// 				// or at least 1/2 of the ring buffer is empty again

// 				for !(bufAvail >= len(bData) || bufAvail >= bufCap/2) {
// 					time.Sleep(time.Millisecond * 10)
// 					p.Lock()
// 					bufAvail = bufCap - p.ring.Length()
// 					p.Unlock()
// 				}

// 				// now we have the space
// 				p.Lock()
// 				counter := 0
// 				for _, frame := range bData {
// 					p.ring.Enqueue(frame)
// 					counter++

// 					bufAvail = bufCap - p.ring.Length()
// 					if bufAvail == 0 {
// 						break
// 					}
// 				}
// 				// remove the frames which were enqueued
// 				bData = bData[counter:]

// 				// update the available space
// 				bufAvail = bufCap - p.ring.Length()
// 				p.Unlock()
// 			}

// 			token.Done()
// 		}()
// 		return nil
// 	}

// 	p.enqueue(bData, msg.EOF)

// 	return nil
// }
<commit_msg>filling buffers to 50% when ringbuffer becomes empty to reduce dropouts<commit_after>package scWriter

import (
	"fmt"
	"log"
	"sync"
	"time"

	ringBuffer "github.com/dh1tw/golang-ring"
	"github.com/dh1tw/gosamplerate"
	"github.com/dh1tw/remoteAudio/audio"
	pa "github.com/gordonklaus/portaudio"
)

// ScWriter implements the audio.Sink interface and is used to write (play)
// audio on a local audio output device (e.g. speakers).
type ScWriter struct {
	sync.RWMutex
	options    Options
	deviceInfo *pa.DeviceInfo
	stream     *pa.Stream
	ring       ringBuffer.Ring
	stash      []float32
	volume     float32
	src        src
	fill       bool
}

// src contains a samplerate converter and its needed variables
type src struct {
	gosamplerate.Src
	samplerate float64
	ratio      float64
}

// NewScWriter returns a new soundcard writer for a specific audio output
// device. This is typically a speaker or a pair of headphones.
func NewScWriter(opts ...Option) (*ScWriter, error) {

	if err := pa.Initialize(); err != nil {
		return nil, err
	}

	info, err := pa.DefaultOutputDevice()
	if err != nil {
		return nil, err
	}

	w := &ScWriter{
		options: Options{
			DeviceName:      "default",
			Channels:        2,
			Samplerate:      48000,
			FramesPerBuffer: 480,
			RingBufferSize:  10,
			Latency:         time.Millisecond * 10,
		},
		deviceInfo: info,
		ring:       ringBuffer.Ring{},
		volume:     0.7,
	}

	for _, option := range opts {
		option(&w.options)
	}

	// setup a samplerate converter
	srConv, err := gosamplerate.New(gosamplerate.SRC_SINC_FASTEST, w.options.Channels, 65536)
	if err != nil {
		return nil, fmt.Errorf("player: %v", err)
	}

	w.src = src{
		Src:        srConv,
		samplerate: w.options.Samplerate,
		ratio:      1,
	}

	// select Playback Audio Device
	if w.options.DeviceName != "default" {
		device, err := getPaDevice(w.options.DeviceName)
		if err != nil {
			return nil, err
		}
		w.deviceInfo = device
	}

	// setup Audio Stream
	streamDeviceParam := pa.StreamDeviceParameters{
		Device:   w.deviceInfo,
		Channels: w.options.Channels,
		Latency:  w.options.Latency,
	}

	streamParm := pa.StreamParameters{
		FramesPerBuffer: w.options.FramesPerBuffer,
		Output:          streamDeviceParam,
		SampleRate:      w.options.Samplerate,
	}

	// setup ring buffer
	w.ring.SetCapacity(w.options.RingBufferSize)

	stream, err := pa.OpenStream(streamParm, w.playCb)
	if err != nil {
		return nil,
			fmt.Errorf("unable to open playback audio stream on device %s: %s",
				w.options.DeviceName, err)
	}

	w.stream = stream

	return w, nil
}

// portaudio callback which will be called continously when the stream is
// started; this function should be short and never block
func (p *ScWriter) playCb(in []float32,
	iTime pa.StreamCallbackTimeInfo,
	iFlags pa.StreamCallbackFlags) {
	switch iFlags {
	case pa.OutputUnderflow:
		log.Println("Output Underflow")
		return // move on!
	case pa.OutputOverflow:
		log.Println("Output Overflow")
		return // move on!
	}

	//pull data from Ringbuffer
	p.Lock()
	data := p.ring.Dequeue()
	bufCapacity := p.ring.Capacity()
	bufLength := p.ring.Length()
	p.Unlock()

	// log.Printf("Buf: %d / %d\n", bufLength, bufCapacity)

	if data == nil && bufLength == 0 {
		if !p.fill {
			// log.Println("start filling buffer")
		}
		p.fill = true
	}

	if bufLength >= bufCapacity/2 && p.fill {
		p.fill = false
		// log.Println("stop filling buffer")
	}

	if data == nil || p.fill {
		// fill with silence
		// fmt.Println("silence")
		for i := 0; i < len(in); i++ {
			in[i] = 0
		}
		return
	}

	audioData := data.([]float32)

	// should never happen
	if len(audioData) != len(in) {
		log.Printf("unable to play audio frame; expected frame size %d, but got %d",
			len(in), len(audioData))
		return
	}

	//copy data into buffer
	copy(in, audioData)
}

// Start starts streaming audio to the Soundcard output device (e.g. Speaker).
func (p *ScWriter) Start() error {
	if p.stream == nil {
		return fmt.Errorf("portaudio stream not initialized")
	}
	return p.stream.Start()
}

// Stop stops streaming audio.
func (p *ScWriter) Stop() error {
	if p.stream == nil {
		return fmt.Errorf("portaudio stream not initialized")
	}
	return p.stream.Stop()
}

// Close shutsdown properly the soundcard audio device.
func (p *ScWriter) Close() error {
	if p.stream == nil {
		return fmt.Errorf("portaudio stream not initialized")
	}
	p.stream.Abort()
	p.stream.Stop()
	return nil
}

// SetVolume sets the volume for all upcoming audio frames.
func (p *ScWriter) SetVolume(v float32) {
	p.Lock()
	defer p.Unlock()
	if v < 0 {
		p.volume = 0
	} else if v > 1 {
		p.volume = 1
	} else {
		p.volume = v
	}
}

// Volume returns the current volume.
func (p *ScWriter) Volume() float32 {
	p.RLock()
	defer p.RUnlock()
	return p.volume
}

// Write converts the frames in the audio buffer into the right format
// and queues them into a ring buffer for playing on the speaker.
func (p *ScWriter) Write(msg audio.Msg) error {

	var aData []float32
	var err error

	// if necessary adjust the amount of audio channels
	if msg.Channels != p.options.Channels {
		aData = audio.AdjustChannels(msg.Channels, p.options.Channels, msg.Data)
	} else {
		aData = msg.Data
	}

	// if necessary, resample the audio
	if msg.Samplerate != p.options.Samplerate {
		if p.src.samplerate != msg.Samplerate {
			p.src.Reset()
			p.src.samplerate = msg.Samplerate
			p.src.ratio = p.options.Samplerate / msg.Samplerate
		}
		aData, err = p.src.Process(aData, p.src.ratio, false)
		if err != nil {
			return err
		}
	}

	// audio buffer size we want to write into our ring buffer
	// (size expected by portaudio callback)
	expBufferSize := p.options.FramesPerBuffer * p.options.Channels

	// if there is data stashed from previous calles, get it and prepend it
	// to the data received
	if len(p.stash) > 0 {
		aData = append(p.stash, aData...)
		p.stash = p.stash[:0] // empty
	}

	if msg.EOF {
		// get the stuff from the stash
		fmt.Println("EOF!!!")
	}

	// if the audio buffer size is actually smaller than the one we need,
	// then stash it for the next time and return
	if len(aData) < expBufferSize {
		p.stash = aData
		return nil
	}

	// slice of audio buffers which will be enqueued into the ring buffer
	var bData [][]float32

	// if the aData contains multiples of the expected buffer size,
	// then we chop it into (several) buffers
	if len(aData) >= expBufferSize {
		p.Lock()
		vol := p.volume
		p.Unlock()

		for len(aData) >= expBufferSize {
			if vol != 1 {
				// if necessary, adjust the volume
				audio.AdjustVolume(vol, aData[:expBufferSize])
			}
			bData = append(bData, aData[:expBufferSize])
			aData = aData[expBufferSize:]
		}
	}

	// stash the left over
	if len(aData) > 0 {
		p.stash = aData
	}

	p.enqueue(bData, msg.EOF)

	return nil
}

func (p *ScWriter) enqueue(bData [][]float32, EOF bool) {
	p.Lock()
	defer p.Unlock()
	for _, frame := range bData {
		p.ring.Enqueue(frame)
	}
}

// Flush clears all internal buffers
func (p *ScWriter) Flush() {
	p.Lock()
	defer p.Unlock()

	// delete the stash
	p.stash = []float32{}

	p.ring = ringBuffer.Ring{}
	p.ring.SetCapacity(p.options.RingBufferSize)
}

// getPaDevice checks if the Audio Devices actually exist and
// then returns it
func getPaDevice(name string) (*pa.DeviceInfo, error) {
	devices, _ := pa.Devices()
	for _, device := range devices {
		if device.Name == name {
			return device, nil
		}
	}
	return nil, fmt.Errorf("unknown audio device %s", name)
}

// Write converts the frames in the audio buffer into the right format
// and queues them into a ring buffer for playing on the speaker. The token is
// used to indicate if the calling application has to wait before it can
// enqueue the next buffer.
// func (p *ScWriter) Write(msg audio.Msg, token audio.Token) error {

// 	var aData []float32
// 	var err error

// 	// if necessary adjust the amount of audio channels
// 	if msg.Channels != p.options.Channels {
// 		aData = audio.AdjustChannels(msg.Channels, p.options.Channels, msg.Data)
// 	} else {
// 		aData = msg.Data
// 	}

// 	// if necessary, resample the audio
// 	if msg.Samplerate != p.options.Samplerate {
// 		if p.src.samplerate != msg.Samplerate {
// 			p.src.Reset()
// 			p.src.samplerate = msg.Samplerate
// 			p.src.ratio = p.options.Samplerate / msg.Samplerate
// 		}
// 		aData, err = p.src.Process(aData, p.src.ratio, false)
// 		if err != nil {
// 			return err
// 		}
// 	}

// 	// audio buffer size we want to write into our ring buffer
// 	// (size expected by portaudio callback)
// 	expBufferSize := p.options.FramesPerBuffer * p.options.Channels

// 	// if there is data stashed from previous calles, get it and prepend it
// 	// to the data received
// 	if len(p.stash) > 0 {
// 		aData = append(p.stash, aData...)
// 		p.stash = p.stash[:0] // empty
// 	}

// 	if msg.EOF {
// 		// get the stuff from the stash
// 		fmt.Println("EOF!!!")
// 		fmt.Println("stash size:", len(p.stash))
// 	}

// 	// if the audio buffer size is actually smaller than the one we need,
// 	// then stash it for the next time and return
// 	if len(aData) < expBufferSize {
// 		p.stash = aData
// 		return nil
// 	}

// 	// slice of audio buffers which will be enqueued into the ring buffer
// 	var bData [][]float32

// 	p.Lock()
// 	bufCap := p.ring.Capacity()
// 	bufAvail := bufCap - p.ring.Length()
// 	p.Unlock()

// 	// if the aData contains multiples of the expected buffer size,
// 	// then we chop it into (several) buffers
// 	if len(aData) >= expBufferSize {
// 		p.Lock()
// 		vol := p.volume
// 		p.Unlock()

// 		for len(aData) >= expBufferSize {
// 			if vol != 1 {
// 				// if necessary, adjust the volume
// 				audio.AdjustVolume(vol, aData[:expBufferSize])
// 			}
// 			bData = append(bData, aData[:expBufferSize])
// 			aData = aData[expBufferSize:]
// 		}
// 	}

// 	// stash the left over
// 	if len(aData) > 0 {
// 		p.stash = aData
// 	}

// 	// if the msg originates from a stream, we ignore the next statement
// 	// and move on (which could mean that we overwrite data in the
// 	// ring buffer - but thats OK to keep latency low)

// 	// in case we don't have a stream (e.g. writing from a file) and the
// 	// ring buffer might be full, we have to wait until there is some
// 	// space available again in the ring buffer
// 	if !msg.IsStream && bufAvail <= len(bData) {

// 		token.Add(1)

// 		go func() {
// 			for len(bData) > 0 {

// 				// wait until there is enough space in the ring buffer,
// 				// or at least 1/2 of the ring buffer is empty again

// 				for !(bufAvail >= len(bData) || bufAvail >= bufCap/2) {
// 					time.Sleep(time.Millisecond * 10)
// 					p.Lock()
// 					bufAvail = bufCap - p.ring.Length()
// 					p.Unlock()
// 				}

// 				// now we have the space
// 				p.Lock()
// 				counter := 0
// 				for _, frame := range bData {
// 					p.ring.Enqueue(frame)
// 					counter++

// 					bufAvail = bufCap - p.ring.Length()
// 					if bufAvail == 0 {
// 						break
// 					}
// 				}
// 				// remove the frames which were enqueued
// 				bData = bData[counter:]

// 				// update the available space
// 				bufAvail = bufCap - p.ring.Length()
// 				p.Unlock()
// 			}

// 			token.Done()
// 		}()
// 		return nil
// 	}

// 	p.enqueue(bData, msg.EOF)

// 	return nil
// }
<|endoftext|>


<commit_before>package opus

import opus "gopkg.in/hraban/opus.v2"

type Option func(*Options)

type Options struct {
	Name         string
	Samplerate   float64
	Channels     int
	Bitrate      int
	MaxBandwidth opus.Bandwidth
	Application  opus.Application
	Complexity   int
}

// Channels is a functional option to set the amount of channels to be used
// with the audio device. Typically this is either Mono (1) or Stereo (2).
// Make sure that your audio device supports the specified amount of channels.
// By default the encoder uses 1 channel, and the decoder 2 channels.
func Channels(chs int) Option {
	return func(args *Options) {
		args.Channels = chs
	}
}

// Samplerate is a functional option to set the sampling rate of the
// audio device. Make sure your audio device supports the specified sampling
// rate. By default, the encoder is set to 48kHz.
func Samplerate(s float64) Option {
	return func(args *Options) {
		args.Samplerate = s
	}
}

// Application is a functional option through which the Encoder profile
// can be selected. By default, RestrictedLowdelay is set. Check the opus
// documentation to learn more about the different profiles.
func Application(a int) Option {
	return func(args *Options) {
		args.Application = opus.Application(a)
	}
}

// MaxBandwidth is a functional option to set the maximum bandpass that the
// encoder can select. Check the opus documentation to learn more about the
// available bandwidths. By default Wideband (8kHz) is selected.
func MaxBandwidth(maxBw int) Option {
	return func(args *Options) {
		args.MaxBandwidth = opus.Bandwidth(maxBw)
	}
}

// Bitrate is a functional option to set the output bitrate of the
// opus encoder. The default value is 24kbit/s.
func Bitrate(rate int) Option {
	return func(args *Options) {
		args.Bitrate = rate
	}
}

// Complexity is a functional option to set the compuational complexity
// of the opus encoder. For the specific values, check the opus encoder
// documentation. By default a complexity of 5 is selected.
func Complexity(c int) Option {
	return func(args *Options) {
		args.Complexity = c
	}
}
<commit_msg>use directly opusCodec types<commit_after>package opus

import opus "gopkg.in/hraban/opus.v2"

type Option func(*Options)

type Options struct {
	Name         string
	Samplerate   float64
	Channels     int
	Bitrate      int
	MaxBandwidth opus.Bandwidth
	Application  opus.Application
	Complexity   int
}

// Channels is a functional option to set the amount of channels to be used
// with the audio device. Typically this is either Mono (1) or Stereo (2).
// Make sure that your audio device supports the specified amount of channels.
// By default the encoder uses 1 channel, and the decoder 2 channels.
func Channels(chs int) Option {
	return func(args *Options) {
		args.Channels = chs
	}
}

// Samplerate is a functional option to set the sampling rate of the
// audio device. Make sure your audio device supports the specified sampling
// rate. By default, the encoder is set to 48kHz.
func Samplerate(s float64) Option {
	return func(args *Options) {
		args.Samplerate = s
	}
}

// Application is a functional option through which the Encoder profile
// can be selected. By default, RestrictedLowdelay is set. Check the opus
// documentation to learn more about the different profiles.
func Application(a opus.Application) Option {
	return func(args *Options) {
		args.Application = a
	}
}

// MaxBandwidth is a functional option to set the maximum bandpass that the
// encoder can select. Check the opus documentation to learn more about the
// available bandwidths. By default Wideband (8kHz) is selected.
func MaxBandwidth(maxBw opus.Bandwidth) Option {
	return func(args *Options) {
		args.MaxBandwidth = maxBw
	}
}

// Bitrate is a functional option to set the output bitrate of the
// opus encoder. The default value is 24kbit/s.
func Bitrate(rate int) Option {
	return func(args *Options) {
		args.Bitrate = rate
	}
}

// Complexity is a functional option to set the compuational complexity
// of the opus encoder. For the specific values, check the opus encoder
// documentation. By default a complexity of 5 is selected.
func Complexity(c int) Option {
	return func(args *Options) {
		args.Complexity = c
	}
}
<|endoftext|>


<commit_before>package statsd

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"strings"
	"time"
)

const metricTypeCount = 'c'
const metricTypeGauge = 'g'
const metricTypeTiming = 't'
const metricTypeSet = 's'

// The Client type
type Client struct {
	host      string
	port      int
	conn      net.Conn          // UDP connection to StatsD server
	rand      *rand.Rand        // rand generator to skip messages by sample rate
	keyBuffer map[string]string // array of messages to send
	autoflush bool              // send metrics on every call
}

// New StatsD client
func NewClient(host string, port int) *Client {
	client := Client{
		host:      host,
		port:      port,
		rand:      rand.New(rand.NewSource(time.Now().Unix())),
		keyBuffer: make(map[string]string),
	}
	return &client
}

// Open UDP connection to statsd server
func (client *Client) Open() {
	connectionString := fmt.Sprintf("%s:%d", client.host, client.port)
	conn, err := net.Dial("udp", connectionString)
	if err != nil {
		log.Println(err)
	}
	client.conn = conn
	client.autoflush = false
}

// Close UDP connection to statsd server
func (client *Client) Close() {
	client.conn.Close()
}

// SetAutoflush enables/disables buffered mode
// In buffered mode requires manual call of Flush()
// In autoflush mode message sends to server on every call
func (client *Client) SetAutoflush(autoflush bool) {
	client.autoflush = autoflush
}

// Timing track in milliseconds with sampling
func (client *Client) Timing(key string, time int64, sampleRate float32) {
	metricValue := fmt.Sprintf("%d|%s", time, metricTypeTiming)
	if sampleRate < 1 {
		if client.isSendAcceptedBySampleRate(sampleRate) {
			metricValue = fmt.Sprintf("%s|@%f", metricValue, sampleRate)
		} else {
			return
		}
	}
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Count tack
func (client *Client) Count(key string, delta int, sampleRate float32) {
	metricValue := fmt.Sprintf("%d|%s", delta, metricTypeCount)
	if sampleRate < 1 {
		if client.isSendAcceptedBySampleRate(sampleRate) {
			metricValue = fmt.Sprintf("%s|@%f", metricValue, sampleRate)
		} else {
			return
		}
	}
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Gauge track
func (client *Client) Gauge(key string, value int) {
	metricValue := fmt.Sprintf("%d|%s", value, metricTypeGauge)
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Set tracking
func (client *Client) Set(key string, value int) {
	metricValue := fmt.Sprintf("%d|%s", value, metricTypeSet)
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Check if acceptable by sample rate
func (client *Client) isSendAcceptedBySampleRate(sampleRate float32) bool {
	if sampleRate >= 1 {
		return true
	}
	randomNumber := client.rand.Float32()
	return randomNumber <= sampleRate
}

// flush data to statsd daemon by UDP
func (client *Client) Flush() {
	// prepare metric packet
	metricPacketArray := make([]string, len(client.keyBuffer))
	for key, metricValue := range client.keyBuffer {
		metricPacketArray = append(metricPacketArray, fmt.Sprintf("%s:%s", key, metricValue))
	}
	metricPacket := strings.Join(metricPacketArray, "|")

	// send metric packet
	_, err := fmt.Fprintf(client.conn, metricPacket)
	if err != nil {
		log.Println(err)
	}
}
<commit_msg>rune const to string<commit_after>package statsd

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"strings"
	"time"
)

const metricTypeCount = "c"
const metricTypeGauge = "g"
const metricTypeTiming = "t"
const metricTypeSet = "s"

// The Client type
type Client struct {
	host      string
	port      int
	conn      net.Conn          // UDP connection to StatsD server
	rand      *rand.Rand        // rand generator to skip messages by sample rate
	keyBuffer map[string]string // array of messages to send
	autoflush bool              // send metrics on every call
}

// New StatsD client
func NewClient(host string, port int) *Client {
	client := Client{
		host:      host,
		port:      port,
		rand:      rand.New(rand.NewSource(time.Now().Unix())),
		keyBuffer: make(map[string]string),
	}
	return &client
}

// Open UDP connection to statsd server
func (client *Client) Open() {
	connectionString := fmt.Sprintf("%s:%d", client.host, client.port)
	conn, err := net.Dial("udp", connectionString)
	if err != nil {
		log.Println(err)
	}
	client.conn = conn
	client.autoflush = false
}

// Close UDP connection to statsd server
func (client *Client) Close() {
	client.conn.Close()
}

// SetAutoflush enables/disables buffered mode
// In buffered mode requires manual call of Flush()
// In autoflush mode message sends to server on every call
func (client *Client) SetAutoflush(autoflush bool) {
	client.autoflush = autoflush
}

// Timing track in milliseconds with sampling
func (client *Client) Timing(key string, time int64, sampleRate float32) {
	metricValue := fmt.Sprintf("%d|%s", time, metricTypeTiming)
	if sampleRate < 1 {
		if client.isSendAcceptedBySampleRate(sampleRate) {
			metricValue = fmt.Sprintf("%s|@%f", metricValue, sampleRate)
		} else {
			return
		}
	}
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Count tack
func (client *Client) Count(key string, delta int, sampleRate float32) {
	metricValue := fmt.Sprintf("%d|%s", delta, metricTypeCount)
	if sampleRate < 1 {
		if client.isSendAcceptedBySampleRate(sampleRate) {
			metricValue = fmt.Sprintf("%s|@%f", metricValue, sampleRate)
		} else {
			return
		}
	}
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Gauge track
func (client *Client) Gauge(key string, value int) {
	metricValue := fmt.Sprintf("%d|%s", value, metricTypeGauge)
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Set tracking
func (client *Client) Set(key string, value int) {
	metricValue := fmt.Sprintf("%d|%s", value, metricTypeSet)
	client.keyBuffer[key] = metricValue
	if client.autoflush {
		client.Flush()
	}
}

// Check if acceptable by sample rate
func (client *Client) isSendAcceptedBySampleRate(sampleRate float32) bool {
	if sampleRate >= 1 {
		return true
	}
	randomNumber := client.rand.Float32()
	return randomNumber <= sampleRate
}

// flush data to statsd daemon by UDP
func (client *Client) Flush() {
	// prepare metric packet
	metricPacketArray := make([]string, len(client.keyBuffer))
	for key, metricValue := range client.keyBuffer {
		metricPacketArray = append(metricPacketArray, fmt.Sprintf("%s:%s", key, metricValue))
	}
	metricPacket := strings.Join(metricPacketArray, "|")

	// send metric packet
	_, err := fmt.Fprintf(client.conn, metricPacket)
	if err != nil {
		log.Println(err)
	}
}
<|endoftext|>


<commit_before>package status

import (
	"bytes"
	"fmt"
	"net/http"
	"runtime"
)

type Code int

var (
	Code_OK                  Code = 0
	Code_INVALID_ARGUMENT    Code = 1
	Code_INTERNAL_ERROR      Code = 2
	Code_UNKNOWN             Code = 3
	Code_NOT_FOUND           Code = 4
	Code_ALREADY_EXISTS      Code = 5
	Code_PERMISSION_DENIED   Code = 6
	Code_UNAUTHENTICATED     Code = 7
	Code_RESOURCE_EXHAUSTED  Code = 8
	Code_FAILED_PRECONDITION Code = 9
	Code_ABORTED             Code = 10
	Code_UNIMPLEMENTED       Code = 11
)

var (
	_codeHttpMapping = map[Code]int{
		Code_OK:                  http.StatusOK,
		Code_INVALID_ARGUMENT:    http.StatusBadRequest,
		Code_INTERNAL_ERROR:      http.StatusInternalServerError,
		Code_UNKNOWN:             http.StatusInternalServerError,
		Code_NOT_FOUND:           http.StatusNotFound,
		Code_ALREADY_EXISTS:      http.StatusConflict,
		Code_PERMISSION_DENIED:   http.StatusForbidden,
		Code_UNAUTHENTICATED:     http.StatusUnauthorized,
		Code_RESOURCE_EXHAUSTED:  429,
		Code_FAILED_PRECONDITION: http.StatusPreconditionFailed,
		Code_ABORTED:             http.StatusConflict,
		Code_UNIMPLEMENTED:       http.StatusNotImplemented,
	}
)

var (
	_codeNameMapping = map[Code]string{
		Code_OK:                  "OK",
		Code_INVALID_ARGUMENT:    "INVALID_ARGUMENT",
		Code_INTERNAL_ERROR:      "INTERNAL_ERROR",
		Code_UNKNOWN:             "UNKNOWN",
		Code_NOT_FOUND:           "NOT_FOUND",
		Code_ALREADY_EXISTS:      "ALREADY_EXISTS",
		Code_PERMISSION_DENIED:   "PERMISSION_DENIED",
		Code_UNAUTHENTICATED:     "UNAUTHENTICATED",
		Code_RESOURCE_EXHAUSTED:  "RESOURCE_EXHAUSTED",
		Code_FAILED_PRECONDITION: "FAILED_PRECONDITION",
		Code_ABORTED:             "ABORTED",
		Code_UNIMPLEMENTED:       "UNIMPLEMENTED",
	}
)

func (c Code) String() string {
	if mapping, present := _codeNameMapping[c]; present {
		return mapping
	}
	return fmt.Sprintf("Unknown Code %d", c)
}

func (c Code) HttpStatus() int {
	if mapping, present := _codeHttpMapping[c]; present {
		return mapping
	}
	return http.StatusInternalServerError
}

type S interface {
	error
	Code() Code
	Message() string
	Cause() error
	Stack() []uintptr
	String() string
	dontImplementMe()
}

func From(err error) S {
	if s, ok := err.(S); ok {
		return s
	}
	return &status{
		code:  Code_INTERNAL_ERROR,
		cause: err,
	}
}

var _ S = &status{}

type status struct {
	code  Code
	msg   string
	cause error
	stack []uintptr
}

func (s *status) Code() Code {
	return s.code
}

func (s *status) Message() string {
	return s.msg
}

func (s *status) Cause() error {
	return s.cause
}

func (s *status) Stack() []uintptr {
	return s.stack
}

func (s *status) Error() string {
	return fmt.Sprintf("%s: %s", s.code, s.msg)
}

func (s *status) String() string {
	var b bytes.Buffer
	s.stringer(&b)
	return b.String()
}

func (s *status) dontImplementMe() {
}

func (s *status) stringer(buf *bytes.Buffer) {
	buf.WriteString(s.Error())
	if len(s.stack) != 0 {
		buf.WriteRune('\n')
		frames := runtime.CallersFrames(s.stack)
		for {
			f, more := frames.Next()
			fmt.Fprintf(buf, "\t%s (%s:%d)", f.Function, f.File, f.Line)
			if !more {
				break
			}
			buf.WriteRune('\n')
		}
	}
	if s.cause == nil {
		return
	}
	buf.WriteString("\nCaused by: ")
	if nexts, ok := s.cause.(*status); ok {
		nexts.stringer(buf)
	} else {
		buf.WriteString(s.cause.Error())
	}
}

func getStack() []uintptr {
	pc := make([]uintptr, 32)
	return pc[:runtime.Callers(2, pc)]
}

func InvalidArgument(e error, v ...interface{}) S {
	return &status{
		code:  Code_INVALID_ARGUMENT,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func InvalidArgumentf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_INVALID_ARGUMENT,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func InternalError(e error, v ...interface{}) S {
	return &status{
		code:  Code_INTERNAL_ERROR,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func InternalErrorf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_INTERNAL_ERROR,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func NotFound(e error, v ...interface{}) S {
	return &status{
		code:  Code_NOT_FOUND,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func NotFoundf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_NOT_FOUND,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func AlreadyExists(e error, v ...interface{}) S {
	return &status{
		code:  Code_ALREADY_EXISTS,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func AlreadyExistsf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_ALREADY_EXISTS,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func Unauthenticated(e error, v ...interface{}) S {
	return &status{
		code:  Code_UNAUTHENTICATED,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func Unauthenticatedf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_UNAUTHENTICATED,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func PermissionDenied(e error, v ...interface{}) S {
	return &status{
		code:  Code_PERMISSION_DENIED,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func PermissionDeniedf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_PERMISSION_DENIED,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func Aborted(e error, v ...interface{}) S {
	return &status{
		code:  Code_ABORTED,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func Abortedf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_ABORTED,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}
<commit_msg>status: update to match code.proto<commit_after>package status

import (
	"bytes"
	"fmt"
	"net/http"
	"runtime"
)

type Code int

var (
	Code_OK                  Code = 0
	Code_CANCELLED           Code = 1
	Code_UNKNOWN             Code = 2
	Code_INVALID_ARGUMENT    Code = 3
	Code_DEADLINE_EXCEEDED   Code = 4
	Code_NOT_FOUND           Code = 5
	Code_ALREADY_EXISTS      Code = 6
	Code_PERMISSION_DENIED   Code = 7
	Code_UNAUTHENTICATED     Code = 16
	Code_RESOURCE_EXHAUSTED  Code = 8
	Code_FAILED_PRECONDITION Code = 9
	Code_ABORTED             Code = 10
	Code_OUT_OF_RANGE        Code = 11
	Code_UNIMPLEMENTED       Code = 12
	Code_INTERNAL            Code = 13
	Code_UNAVAILABLE         Code = 14
	Code_DATA_LOSS           Code = 15
)

var (
	_codeHttpMapping = map[Code]int{
		Code_OK:                  http.StatusOK,
		Code_CANCELLED:           499, // Client Closed Request
		Code_UNKNOWN:             http.StatusInternalServerError,
		Code_INVALID_ARGUMENT:    http.StatusBadRequest,
		Code_DEADLINE_EXCEEDED:   http.StatusGatewayTimeout,
		Code_NOT_FOUND:           http.StatusNotFound,
		Code_ALREADY_EXISTS:      http.StatusConflict,
		Code_PERMISSION_DENIED:   http.StatusForbidden,
		Code_UNAUTHENTICATED:     http.StatusUnauthorized,
		Code_RESOURCE_EXHAUSTED:  http.StatusTooManyRequests,
		Code_FAILED_PRECONDITION: http.StatusPreconditionFailed, // not 400, as code.proto suggests
		Code_ABORTED:             http.StatusConflict,
		Code_OUT_OF_RANGE:        http.StatusRequestedRangeNotSatisfiable, // not 400, as code.proto suggests
		Code_UNIMPLEMENTED:       http.StatusNotImplemented,
		Code_INTERNAL:            http.StatusInternalServerError,
		Code_UNAVAILABLE:         http.StatusServiceUnavailable,
		Code_DATA_LOSS:           http.StatusInternalServerError,
	}
)

var (
	_codeNameMapping = map[Code]string{
		Code_OK:                  "OK",
		Code_CANCELLED:           "CANCELLED",
		Code_UNKNOWN:             "UNKNOWN",
		Code_INVALID_ARGUMENT:    "INVALID_ARGUMENT",
		Code_DEADLINE_EXCEEDED:   "DEADLINE_EXCEEDED",
		Code_NOT_FOUND:           "NOT_FOUND",
		Code_ALREADY_EXISTS:      "ALREADY_EXISTS",
		Code_PERMISSION_DENIED:   "PERMISSION_DENIED",
		Code_UNAUTHENTICATED:     "UNAUTHENTICATED",
		Code_RESOURCE_EXHAUSTED:  "RESOURCE_EXHAUSTED",
		Code_FAILED_PRECONDITION: "FAILED_PRECONDITION",
		Code_ABORTED:             "ABORTED",
		Code_OUT_OF_RANGE:        "OUT_OF_RANGE",
		Code_UNIMPLEMENTED:       "UNIMPLEMENTED",
		Code_INTERNAL:            "INTERNAL",
		Code_UNAVAILABLE:         "UNAVAILABLE",
		Code_DATA_LOSS:           "DATA_LOSS",
	}
)

func (c Code) String() string {
	if mapping, present := _codeNameMapping[c]; present {
		return mapping
	}
	return fmt.Sprintf("Unknown Code %d", c)
}

func (c Code) HttpStatus() int {
	if mapping, present := _codeHttpMapping[c]; present {
		return mapping
	}
	return http.StatusInternalServerError
}

type S interface {
	error
	Code() Code
	Message() string
	Cause() error
	Stack() []uintptr
	String() string
	dontImplementMe()
}

func From(err error) S {
	if s, ok := err.(S); ok {
		return s
	}
	return &status{
		code:  Code_UNKNOWN,
		cause: err,
	}
}

var _ S = &status{}

type status struct {
	code  Code
	msg   string
	cause error
	stack []uintptr
}

func (s *status) Code() Code {
	return s.code
}

func (s *status) Message() string {
	return s.msg
}

func (s *status) Cause() error {
	return s.cause
}

func (s *status) Stack() []uintptr {
	return s.stack
}

func (s *status) Error() string {
	return fmt.Sprintf("%s: %s", s.code, s.msg)
}

func (s *status) String() string {
	var b bytes.Buffer
	s.stringer(&b)
	return b.String()
}

func (s *status) dontImplementMe() {
}

func (s *status) stringer(buf *bytes.Buffer) {
	buf.WriteString(s.Error())
	if len(s.stack) != 0 {
		buf.WriteRune('\n')
		frames := runtime.CallersFrames(s.stack)
		for {
			f, more := frames.Next()
			fmt.Fprintf(buf, "\t%s (%s:%d)", f.Function, f.File, f.Line)
			if !more {
				break
			}
			buf.WriteRune('\n')
		}
	}
	if s.cause == nil {
		return
	}
	buf.WriteString("\nCaused by: ")
	if nexts, ok := s.cause.(*status); ok {
		nexts.stringer(buf)
	} else {
		buf.WriteString(s.cause.Error())
	}
}

func getStack() []uintptr {
	pc := make([]uintptr, 32)
	return pc[:runtime.Callers(2, pc)]
}

func InvalidArgument(e error, v ...interface{}) S {
	return &status{
		code:  Code_INVALID_ARGUMENT,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func InvalidArgumentf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_INVALID_ARGUMENT,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func InternalError(e error, v ...interface{}) S {
	return &status{
		code:  Code_INTERNAL,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func InternalErrorf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_INTERNAL,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func NotFound(e error, v ...interface{}) S {
	return &status{
		code:  Code_NOT_FOUND,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func NotFoundf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_NOT_FOUND,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func AlreadyExists(e error, v ...interface{}) S {
	return &status{
		code:  Code_ALREADY_EXISTS,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func AlreadyExistsf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_ALREADY_EXISTS,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func Unauthenticated(e error, v ...interface{}) S {
	return &status{
		code:  Code_UNAUTHENTICATED,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func Unauthenticatedf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_UNAUTHENTICATED,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func PermissionDenied(e error, v ...interface{}) S {
	return &status{
		code:  Code_PERMISSION_DENIED,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func PermissionDeniedf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_PERMISSION_DENIED,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func Aborted(e error, v ...interface{}) S {
	return &status{
		code:  Code_ABORTED,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func Abortedf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_ABORTED,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}

func Unimplemented(e error, v ...interface{}) S {
	return &status{
		code:  Code_UNIMPLEMENTED,
		msg:   fmt.Sprint(v...),
		cause: e,
		stack: getStack(),
	}
}

func Unimplementedf(e error, format string, v ...interface{}) S {
	return &status{
		code:  Code_UNIMPLEMENTED,
		msg:   fmt.Sprintf(format, v...),
		cause: e,
		stack: getStack(),
	}
}
<|endoftext|>


<commit_before>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/apache/incubator-trafficcontrol/lib/go-log"
	"github.com/apache/incubator-trafficcontrol/lib/go-tc"
	"github.com/jmoiron/sqlx"
)

func getServerUpdateStatusHandler(db *sqlx.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		handleErr := tc.GetHandleErrorFunc(w, r)

		// p PathParams, username string, privLevel int
		ctx := r.Context()
		pathParams, err := getPathParams(ctx)
		if err != nil {
			handleErr(err, http.StatusInternalServerError)
			return
		}
		hostName := pathParams["host_name"]

		serverUpdateStatus, err := getServerUpdateStatus(hostName, db)
		if err != nil {
			handleErr(err, http.StatusInternalServerError)
			return
		}

		respBts, err := json.Marshal(serverUpdateStatus)
		if err != nil {
			handleErr(err, http.StatusInternalServerError)
			return
		}

		w.Header().Set(tc.ContentType, tc.ApplicationJson)
		fmt.Fprintf(w, "%s", respBts)
	}
}

func getServerUpdateStatus(hostName string, db *sqlx.DB) ([]tc.ServerUpdateStatus, error) {
	baseSelectStatement :=
		`WITH parentservers AS (SELECT ps.id, ps.cachegroup, ps.cdn_id, ps.upd_pending, ps.reval_pending FROM server ps
         LEFT JOIN status AS pstatus ON pstatus.id = ps.status
         WHERE pstatus.name != 'OFFLINE' ),
         use_reval AS (SELECT value::boolean FROM parameter WHERE name = 'use_reval_pending' AND config_file = 'global')
         SELECT s.id, s.host_name, type.name AS type, (s.reval_pending::boolean AND use_reval.value) as combined_reval_pending, s.upd_pending, status.name AS status, COALESCE(bool_or(ps.upd_pending), FALSE) AS parent_upd_pending, COALESCE(bool_or(ps.reval_pending), FALSE) AS parent_reval_pending FROM use_reval, server s
         LEFT JOIN status ON s.status = status.id
         LEFT JOIN cachegroup cg ON s.cachegroup = cg.id
         LEFT JOIN type ON type.id = s.type
         LEFT JOIN parentservers ps ON ps.cachegroup = cg.parent_cachegroup_id AND ps.cdn_id = s.cdn_id AND type.name = 'EDGE'` //remove the EDGE reference if other server types should have their parents processed

	groupBy := ` GROUP BY s.id, s.host_name, type.name, combined_reval_pending, s.upd_pending, status.name ORDER BY s.id;`

	updateStatuses := []tc.ServerUpdateStatus{}
	var rows *sql.Rows
	var err error
	if hostName == "all" {
		rows, err = db.Query(baseSelectStatement + groupBy)
		if err != nil {
			log.Error.Printf("could not execute select server update status query: %s\n", err)
			return nil, tc.DBError
		}
	} else {
		rows, err = db.Query(baseSelectStatement + ` WHERE s.host_name = $1` + groupBy, hostName)
		if err != nil {
			log.Error.Printf("could not execute select server update status by hostname query: %s\n", err)
			return nil, tc.DBError
		}
	}
	defer rows.Close()

	for rows.Next() {
		var serverUpdateStatus tc.ServerUpdateStatus
		var serverType string
		if err := rows.Scan(&serverUpdateStatus.HostId, &serverUpdateStatus.HostName, &serverType, &serverUpdateStatus.RevalPending, &serverUpdateStatus.UpdatePending, &serverUpdateStatus.Status, &serverUpdateStatus.ParentPending, &serverUpdateStatus.ParentRevalPending); err != nil {
			log.Error.Printf("could not scan server update status: %s\n", err)
			return nil, tc.DBError
		}
		if hostName == "all" { //if we want to return the parent data for servers when all is used remove this block
			serverUpdateStatus.ParentRevalPending = false
			serverUpdateStatus.ParentPending = false
		}
		updateStatuses = append(updateStatuses, serverUpdateStatus)
	}
	return updateStatuses, nil
}
<commit_msg>default use_reval_pending to false so query doesn't fail when parameter isn't set<commit_after>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/apache/incubator-trafficcontrol/lib/go-log"
	"github.com/apache/incubator-trafficcontrol/lib/go-tc"
	"github.com/jmoiron/sqlx"
)

func getServerUpdateStatusHandler(db *sqlx.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		handleErr := tc.GetHandleErrorFunc(w, r)

		// p PathParams, username string, privLevel int
		ctx := r.Context()
		pathParams, err := getPathParams(ctx)
		if err != nil {
			handleErr(err, http.StatusInternalServerError)
			return
		}
		hostName := pathParams["host_name"]

		serverUpdateStatus, err := getServerUpdateStatus(hostName, db)
		if err != nil {
			handleErr(err, http.StatusInternalServerError)
			return
		}

		respBts, err := json.Marshal(serverUpdateStatus)
		if err != nil {
			handleErr(err, http.StatusInternalServerError)
			return
		}

		w.Header().Set(tc.ContentType, tc.ApplicationJson)
		fmt.Fprintf(w, "%s", respBts)
	}
}

func getServerUpdateStatus(hostName string, db *sqlx.DB) ([]tc.ServerUpdateStatus, error) {
	baseSelectStatement :=
		`WITH parentservers AS (SELECT ps.id, ps.cachegroup, ps.cdn_id, ps.upd_pending, ps.reval_pending FROM server ps
         LEFT JOIN status AS pstatus ON pstatus.id = ps.status
         WHERE pstatus.name != 'OFFLINE' ),
         use_reval AS (SELECT COALESCE(value::boolean, FALSE) FROM parameter WHERE name = 'use_reval_pending' AND config_file = 'global')
         SELECT s.id, s.host_name, type.name AS type, (s.reval_pending::boolean AND use_reval.value) as combined_reval_pending, s.upd_pending, status.name AS status, COALESCE(bool_or(ps.upd_pending), FALSE) AS parent_upd_pending, COALESCE(bool_or(ps.reval_pending), FALSE) AS parent_reval_pending FROM use_reval, server s
         LEFT JOIN status ON s.status = status.id
         LEFT JOIN cachegroup cg ON s.cachegroup = cg.id
         LEFT JOIN type ON type.id = s.type
         LEFT JOIN parentservers ps ON ps.cachegroup = cg.parent_cachegroup_id AND ps.cdn_id = s.cdn_id AND type.name = 'EDGE'` //remove the EDGE reference if other server types should have their parents processed

	groupBy := ` GROUP BY s.id, s.host_name, type.name, combined_reval_pending, s.upd_pending, status.name ORDER BY s.id;`

	updateStatuses := []tc.ServerUpdateStatus{}
	var rows *sql.Rows
	var err error
	if hostName == "all" {
		rows, err = db.Query(baseSelectStatement + groupBy)
		if err != nil {
			log.Error.Printf("could not execute select server update status query: %s\n", err)
			return nil, tc.DBError
		}
	} else {
		rows, err = db.Query(baseSelectStatement + ` WHERE s.host_name = $1` + groupBy, hostName)
		if err != nil {
			log.Error.Printf("could not execute select server update status by hostname query: %s\n", err)
			return nil, tc.DBError
		}
	}
	defer rows.Close()

	for rows.Next() {
		var serverUpdateStatus tc.ServerUpdateStatus
		var serverType string
		if err := rows.Scan(&serverUpdateStatus.HostId, &serverUpdateStatus.HostName, &serverType, &serverUpdateStatus.RevalPending, &serverUpdateStatus.UpdatePending, &serverUpdateStatus.Status, &serverUpdateStatus.ParentPending, &serverUpdateStatus.ParentRevalPending); err != nil {
			log.Error.Printf("could not scan server update status: %s\n", err)
			return nil, tc.DBError
		}
		if hostName == "all" { //if we want to return the parent data for servers when all is used remove this block
			serverUpdateStatus.ParentRevalPending = false
			serverUpdateStatus.ParentPending = false
		}
		updateStatuses = append(updateStatuses, serverUpdateStatus)
	}
	return updateStatuses, nil
}
<|endoftext|>


<commit_before>// Use of this source code is governed by a MIT license that can be found in the LICENSE file.
// Giorgis (habtom@giorgis.io)

package avformat

//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import (
	"unsafe"
)

type IO C.struct_AVCodecContext

func (ctxt *Context) Chapters() **AvChapter {
	return (**AvChapter)(unsafe.Pointer(ctxt.chapters))
}

func (ctxt *Context) AudioCodec() *AvCodec {
	return (*AvCodec)(unsafe.Pointer(ctxt.audio_codec))
}

func (ctxt *Context) SubtitleCodec() *AvCodec {
	return (*AvCodec)(unsafe.Pointer(ctxt.subtitle_codec))
}

func (ctxt *Context) VideoCodec() *AvCodec {
	return (*AvCodec)(unsafe.Pointer(ctxt.video_codec))
}

func (ctxt *Context) Metadata() *Dictionary {
	return (*Dictionary)(unsafe.Pointer(ctxt.metadata))
}

func (ctxt *Context) Internal() *AvFormatInternal {
	return (*AvFormatInternal)(unsafe.Pointer(ctxt.internal))
}

func (ctxt *Context) Pb() *AvIOContext {
	return (*AvIOContext)(unsafe.Pointer(ctxt.pb))
}

func (ctxt *Context) InterruptCallback() AvIOInterruptCB {
	return AvIOInterruptCB(ctxt.interrupt_callback)
}

func (ctxt *Context) Programs() **AvProgram {
	return (**AvProgram)(unsafe.Pointer(ctxt.programs))
}

func (ctxt *Context) Streams(i uint) *Stream {
	offset := (unsafe.Sizeof(unsafe.Pointer(*ctxt.streams)) * uintptr(i))
	return *(** Stream)(unsafe.Pointer(uintptr(unsafe.Pointer(ctxt.streams)) + offset ))
}

func (ctxt *Context) Filename() string {
	return C.GoString((*C.char)(unsafe.Pointer(&ctxt.filename[0])))
}

// func (ctxt *Context) CodecWhitelist() string {
// 	return C.GoString(ctxt.codec_whitelist)
// }

// func (ctxt *Context) FormatWhitelist() string {
// 	return C.GoString(ctxt.format_whitelist)
// }

func (ctxt *Context) AudioCodecId() CodecId {
	return CodecId(ctxt.audio_codec_id)
}

func (ctxt *Context) SubtitleCodecId() CodecId {
	return CodecId(ctxt.subtitle_codec_id)
}

func (ctxt *Context) VideoCodecId() CodecId {
	return CodecId(ctxt.video_codec_id)
}

func (ctxt *Context) DurationEstimationMethod() AvDurationEstimationMethod {
	return AvDurationEstimationMethod(ctxt.duration_estimation_method)
}

func (ctxt *Context) AudioPreload() int {
	return int(ctxt.audio_preload)
}

func (ctxt *Context) AvioFlags() int {
	return int(ctxt.avio_flags)
}

func (ctxt *Context) AvoidNegativeTs() int {
	return int(ctxt.avoid_negative_ts)
}

func (ctxt *Context) BitRate() int {
	return int(ctxt.bit_rate)
}

func (ctxt *Context) CtxFlags() int {
	return int(ctxt.ctx_flags)
}

func (ctxt *Context) Debug() int {
	return int(ctxt.debug)
}

func (ctxt *Context) ErrorRecognition() int {
	return int(ctxt.error_recognition)
}

func (ctxt *Context) EventFlags() int {
	return int(ctxt.event_flags)
}

func (ctxt *Context) Flags() int {
	return int(ctxt.flags)
}

func (ctxt *Context) FlushPackets() int {
	return int(ctxt.flush_packets)
}

func (ctxt *Context) FormatProbesize() int {
	return int(ctxt.format_probesize)
}

func (ctxt *Context) FpsProbeSize() int {
	return int(ctxt.fps_probe_size)
}

func (ctxt *Context) IoRepositioned() int {
	return int(ctxt.io_repositioned)
}

func (ctxt *Context) Keylen() int {
	return int(ctxt.keylen)
}

func (ctxt *Context) MaxChunkDuration() int {
	return int(ctxt.max_chunk_duration)
}

func (ctxt *Context) MaxChunkSize() int {
	return int(ctxt.max_chunk_size)
}

func (ctxt *Context) MaxDelay() int {
	return int(ctxt.max_delay)
}

func (ctxt *Context) MaxTsProbe() int {
	return int(ctxt.max_ts_probe)
}

func (ctxt *Context) MetadataHeaderPadding() int {
	return int(ctxt.metadata_header_padding)
}

func (ctxt *Context) ProbeScore() int {
	return int(ctxt.probe_score)
}

func (ctxt *Context) Seek2any() int {
	return int(ctxt.seek2any)
}

func (ctxt *Context) StrictStdCompliance() int {
	return int(ctxt.strict_std_compliance)
}

func (ctxt *Context) TsId() int {
	return int(ctxt.ts_id)
}

func (ctxt *Context) UseWallclockAsTimestamps() int {
	return int(ctxt.use_wallclock_as_timestamps)
}

func (ctxt *Context) Duration() int64 {
	return int64(ctxt.duration)
}

func (ctxt *Context) MaxAnalyzeDuration2() int64 {
	return int64(ctxt.max_analyze_duration2)
}

func (ctxt *Context) MaxInterleaveDelta() int64 {
	return int64(ctxt.max_interleave_delta)
}

func (ctxt *Context) OutputTsOffset() int64 {
	return int64(ctxt.output_ts_offset)
}

func (ctxt *Context) Probesize2() int64 {
	return int64(ctxt.probesize2)
}

func (ctxt *Context) SkipInitialBytes() int64 {
	return int64(ctxt.skip_initial_bytes)
}

func (ctxt *Context) StartTime() int64 {
	return int64(ctxt.start_time)
}

func (ctxt *Context) StartTimeRealtime() int64 {
	return int64(ctxt.start_time_realtime)
}

func (ctxt *Context) Iformat() *InputFormat {
	return (*InputFormat)(unsafe.Pointer(ctxt.iformat))
}

func (ctxt *Context) Oformat() *OutputFormat {
	return (*OutputFormat)(unsafe.Pointer(ctxt.oformat))
}

// func (ctxt *Context) DumpSeparator() uint8 {
// 	return uint8(ctxt.dump_separator)
// }

func (ctxt *Context) CorrectTsOverflow() int {
	return int(ctxt.correct_ts_overflow)
}

func (ctxt *Context) MaxIndexSize() uint {
	return uint(ctxt.max_index_size)
}

func (ctxt *Context) MaxPictureBuffer() uint {
	return uint(ctxt.max_picture_buffer)
}

func (ctxt *Context) NbChapters() uint {
	return uint(ctxt.nb_chapters)
}

func (ctxt *Context) NbPrograms() uint {
	return uint(ctxt.nb_programs)
}

func (ctxt *Context) NbStreams() uint {
	return uint(ctxt.nb_streams)
}

func (ctxt *Context) PacketSize() uint {
	return uint(ctxt.packet_size)
}

func (ctxt *Context) Probesize() uint {
	return uint(ctxt.probesize)
}
<commit_msg>Remove accidental line creation<commit_after>// Use of this source code is governed by a MIT license that can be found in the LICENSE file.
// Giorgis (habtom@giorgis.io)

package avformat

//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import (
	"unsafe"
)

func (ctxt *Context) Chapters() **AvChapter {
	return (**AvChapter)(unsafe.Pointer(ctxt.chapters))
}

func (ctxt *Context) AudioCodec() *AvCodec {
	return (*AvCodec)(unsafe.Pointer(ctxt.audio_codec))
}

func (ctxt *Context) SubtitleCodec() *AvCodec {
	return (*AvCodec)(unsafe.Pointer(ctxt.subtitle_codec))
}

func (ctxt *Context) VideoCodec() *AvCodec {
	return (*AvCodec)(unsafe.Pointer(ctxt.video_codec))
}

func (ctxt *Context) Metadata() *Dictionary {
	return (*Dictionary)(unsafe.Pointer(ctxt.metadata))
}

func (ctxt *Context) Internal() *AvFormatInternal {
	return (*AvFormatInternal)(unsafe.Pointer(ctxt.internal))
}

func (ctxt *Context) Pb() *AvIOContext {
	return (*AvIOContext)(unsafe.Pointer(ctxt.pb))
}

func (ctxt *Context) InterruptCallback() AvIOInterruptCB {
	return AvIOInterruptCB(ctxt.interrupt_callback)
}

func (ctxt *Context) Programs() **AvProgram {
	return (**AvProgram)(unsafe.Pointer(ctxt.programs))
}

func (ctxt *Context) Streams(i uint) *Stream {
	offset := (unsafe.Sizeof(unsafe.Pointer(*ctxt.streams)) * uintptr(i))
	return *(** Stream)(unsafe.Pointer(uintptr(unsafe.Pointer(ctxt.streams)) + offset ))
}

func (ctxt *Context) Filename() string {
	return C.GoString((*C.char)(unsafe.Pointer(&ctxt.filename[0])))
}

// func (ctxt *Context) CodecWhitelist() string {
// 	return C.GoString(ctxt.codec_whitelist)
// }

// func (ctxt *Context) FormatWhitelist() string {
// 	return C.GoString(ctxt.format_whitelist)
// }

func (ctxt *Context) AudioCodecId() CodecId {
	return CodecId(ctxt.audio_codec_id)
}

func (ctxt *Context) SubtitleCodecId() CodecId {
	return CodecId(ctxt.subtitle_codec_id)
}

func (ctxt *Context) VideoCodecId() CodecId {
	return CodecId(ctxt.video_codec_id)
}

func (ctxt *Context) DurationEstimationMethod() AvDurationEstimationMethod {
	return AvDurationEstimationMethod(ctxt.duration_estimation_method)
}

func (ctxt *Context) AudioPreload() int {
	return int(ctxt.audio_preload)
}

func (ctxt *Context) AvioFlags() int {
	return int(ctxt.avio_flags)
}

func (ctxt *Context) AvoidNegativeTs() int {
	return int(ctxt.avoid_negative_ts)
}

func (ctxt *Context) BitRate() int {
	return int(ctxt.bit_rate)
}

func (ctxt *Context) CtxFlags() int {
	return int(ctxt.ctx_flags)
}

func (ctxt *Context) Debug() int {
	return int(ctxt.debug)
}

func (ctxt *Context) ErrorRecognition() int {
	return int(ctxt.error_recognition)
}

func (ctxt *Context) EventFlags() int {
	return int(ctxt.event_flags)
}

func (ctxt *Context) Flags() int {
	return int(ctxt.flags)
}

func (ctxt *Context) FlushPackets() int {
	return int(ctxt.flush_packets)
}

func (ctxt *Context) FormatProbesize() int {
	return int(ctxt.format_probesize)
}

func (ctxt *Context) FpsProbeSize() int {
	return int(ctxt.fps_probe_size)
}

func (ctxt *Context) IoRepositioned() int {
	return int(ctxt.io_repositioned)
}

func (ctxt *Context) Keylen() int {
	return int(ctxt.keylen)
}

func (ctxt *Context) MaxChunkDuration() int {
	return int(ctxt.max_chunk_duration)
}

func (ctxt *Context) MaxChunkSize() int {
	return int(ctxt.max_chunk_size)
}

func (ctxt *Context) MaxDelay() int {
	return int(ctxt.max_delay)
}

func (ctxt *Context) MaxTsProbe() int {
	return int(ctxt.max_ts_probe)
}

func (ctxt *Context) MetadataHeaderPadding() int {
	return int(ctxt.metadata_header_padding)
}

func (ctxt *Context) ProbeScore() int {
	return int(ctxt.probe_score)
}

func (ctxt *Context) Seek2any() int {
	return int(ctxt.seek2any)
}

func (ctxt *Context) StrictStdCompliance() int {
	return int(ctxt.strict_std_compliance)
}

func (ctxt *Context) TsId() int {
	return int(ctxt.ts_id)
}

func (ctxt *Context) UseWallclockAsTimestamps() int {
	return int(ctxt.use_wallclock_as_timestamps)
}

func (ctxt *Context) Duration() int64 {
	return int64(ctxt.duration)
}

func (ctxt *Context) MaxAnalyzeDuration2() int64 {
	return int64(ctxt.max_analyze_duration2)
}

func (ctxt *Context) MaxInterleaveDelta() int64 {
	return int64(ctxt.max_interleave_delta)
}

func (ctxt *Context) OutputTsOffset() int64 {
	return int64(ctxt.output_ts_offset)
}

func (ctxt *Context) Probesize2() int64 {
	return int64(ctxt.probesize2)
}

func (ctxt *Context) SkipInitialBytes() int64 {
	return int64(ctxt.skip_initial_bytes)
}

func (ctxt *Context) StartTime() int64 {
	return int64(ctxt.start_time)
}

func (ctxt *Context) StartTimeRealtime() int64 {
	return int64(ctxt.start_time_realtime)
}

func (ctxt *Context) Iformat() *InputFormat {
	return (*InputFormat)(unsafe.Pointer(ctxt.iformat))
}

func (ctxt *Context) Oformat() *OutputFormat {
	return (*OutputFormat)(unsafe.Pointer(ctxt.oformat))
}

// func (ctxt *Context) DumpSeparator() uint8 {
// 	return uint8(ctxt.dump_separator)
// }

func (ctxt *Context) CorrectTsOverflow() int {
	return int(ctxt.correct_ts_overflow)
}

func (ctxt *Context) MaxIndexSize() uint {
	return uint(ctxt.max_index_size)
}

func (ctxt *Context) MaxPictureBuffer() uint {
	return uint(ctxt.max_picture_buffer)
}

func (ctxt *Context) NbChapters() uint {
	return uint(ctxt.nb_chapters)
}

func (ctxt *Context) NbPrograms() uint {
	return uint(ctxt.nb_programs)
}

func (ctxt *Context) NbStreams() uint {
	return uint(ctxt.nb_streams)
}

func (ctxt *Context) PacketSize() uint {
	return uint(ctxt.packet_size)
}

func (ctxt *Context) Probesize() uint {
	return uint(ctxt.probesize)
}
<|endoftext|>


<commit_before>// imsto core objects
package storage

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"path"
	"time"
	"wpst.me/calf/config"
	cdb "wpst.me/calf/db"
	iimg "wpst.me/calf/image"
	"wpst.me/calf/storage/backend"
)

type AppId uint16

type Author uint32

type Entry struct {
	Id       *EntryId   `json:"id,omitempty"`
	Name     string     `json:"name,omitempty"`
	Size     uint32     `json:"size"`
	Path     string     `json:"path"`
	Mime     string     `json:"mime,omitempty"`
	Status   uint8      `json:"-"`
	Hashes   cdb.Qarray `json:"-"`
	Ids      cdb.Qarray `json:"-"`
	Roofs    cdb.Qarray `json:"roofs"`
	Tags     cdb.Qarray `json:"tags,omitempty"`
	Meta     *iimg.Attr `json:"meta,omitempty"`
	AppId    AppId      `json:"appid,omitempty"`
	Author   Author     `json:"author,omitempty"`
	Modified uint64     `json:"modified,omitempty"`
	Created  time.Time  `json:"created,omitempty"`
	exif     cdb.Hstore
	sev      cdb.Hstore
	b        []byte
	h        string
	_treked  bool
	ret      int       // db saved result
	Done     chan bool `json:"-"`
	ready    int
}

const (
	min_size = 43
)

func NewEntry(data []byte, name string) (e *Entry, err error) {
	if len(data) < min_size {
		err = errors.New("data is too small, maybe not a valid image")
		return
	}

	var id *EntryId
	id, err = NewEntryIdFromData(data)

	if err != nil {
		log.Println(err)
		return
	}

	e = &Entry{
		Id:      id,
		Name:    name,
		Size:    uint32(len(data)),
		Created: time.Now(),
		b:       data,
		h:       id.hash,
	}

	// entry = &Entry{Id: id, Name: name, Size: ia.Size, Meta: ia, Path: path, Mime: mimetype, Hashes: hashes, Ids: ids}

	return
}

// 处理图片信息并填充
func (e *Entry) Trek(roof string) (err error) {
	if e._treked {
		return
	}
	e._treked = true
	var im iimg.Image
	rd := bytes.NewReader(e.b)
	im, err = iimg.Open(rd)

	if err != nil {
		log.Printf("image open error: %s", err)
		return
	}

	defer im.Close()

	ia := im.GetAttr()
	// log.Println(ia)

	max_quality := iimg.Quality(config.GetInt(roof, "max_quality"))
	if ia.Quality > max_quality {
		log.Printf("jpeg quality %d is too high, set to %d", ia.Quality, max_quality)
	} else {
		max_quality = ia.Quality
		log.Printf("jpeg quality %d is too low", ia.Quality)
	}
	im.SetOption(iimg.WriteOption{Quality: max_quality, StripAll: true})

	max_width := iimg.Dimension(config.GetInt(roof, "max_width"))
	max_height := iimg.Dimension(config.GetInt(roof, "max_height"))
	if ia.Width > max_width || ia.Height > max_height {
		err = fmt.Errorf("dimension %dx%d of %s is too big", ia.Width, ia.Height, e.Name)
		return
	}

	min_width := iimg.Dimension(config.GetInt(roof, "min_width"))
	min_height := iimg.Dimension(config.GetInt(roof, "min_height"))
	if ia.Width < min_width || ia.Height < min_height {
		err = fmt.Errorf("dimension %dx%d of %s is too small", ia.Width, ia.Height, e.Name)
		return
	}

	var data []byte
	data, err = im.GetBlob() // tack new data

	if err != nil {
		log.Printf("GetBlob error: %s", err)
		return
	}

	hashes := cdb.Qarray{e.h}
	ids := cdb.Qarray{e.Id.String()}

	size := len(data)
	if max_file_size := config.GetInt(roof, "max_file_size"); size > max_file_size {
		err = fmt.Errorf("file: %s size %d is too big, max is %d", e.Name, size, max_file_size)
		return
	}

	var id2 *EntryId
	id2, err = NewEntryIdFromData(data)
	if id2.hash != e.h {
		hashes = append(hashes, id2.hash)
		if err != nil {
			// log.Println(err)
			return
		}
		ids = append(ids, id2.id)
		e.Id = id2 // 使用新的 Id 作为主键
		e.h = id2.hash
		e.b = data
		e.Size = uint32(size)
	}

	ia.Size = iimg.Size(size) // 更新后的大小
	ia.Name = e.Name

	path := newPath(e.Id, ia.Ext)

	log.Printf("ext: %s, mime: %s\n", ia.Ext, ia.Mime)

	e.Meta = ia
	e.Path = path
	e.Mime = ia.Mime
	e.Hashes = hashes
	e.Ids = ids
	return
}

// return hash value string
// func (e *Entry) Hashed() string {
// 	return e.h
// }

// return binary bytes
func (e *Entry) Blob() []byte {
	return e.b
}

func (e *Entry) IsDone() bool {
	return e.ready != 1
}

func (e *Entry) Store(roof string) (err error) {

	mw := NewMetaWrapper(roof)
	eh, _err := mw.GetHash(e.h)
	if _err != nil { // ok, not exsits
		log.Printf("check hash error: %s", _err)
	} else if eh != nil && eh.id != "" {
		if _id, _err := NewEntryId(eh.id); _err == nil {
			e.Id = _id
			_ne, _err := mw.GetEntry(*_id)
			if _err == nil { // path, mime, size, sev, status, created
				if _ne.Roofs.Contains(roof) {
					e.Name = _ne.Name
					e.Path = _ne.Path
					e.Size = _ne.Size
					e.Mime = _ne.Mime
					e.Meta = _ne.Meta
					// e.sev = _ne.sev
					e.Created = _ne.Created
					e.Roofs = _ne.Roofs
					e.reset()
					e._treked = true

					log.Printf("exist: %s, %s", e.Id, e.Path)
					return
				}

				log.Printf("[%s]%s not in %s, so resubmit it", roof, e.Id, _ne.Roofs)

				// for _, _roof := range _ne.Roofs {
				// 	_mw := NewMetaWrapper(fmt.Sprint(_roof))
				// 	t, te := _mw.GetMeta(*_ne.Id)
				// 	if te == nil {
				// 		e = t
				// 		err = mw.Save(t)
				// 		return
				// 	}
				// }

				// e.Done <- true
			} else {
				log.Printf("get entry error: %s", _err)
			}
		}
	}

	if err = e.Trek(roof); err != nil {
		return
	}
	log.Printf("new id: %v, size: %d, path: %v\n", e.Id, e.Size, e.Path)

	data := e.Blob()
	// size := len(data)
	// log.Printf("blob length: %d", size)
	thumb_root := config.GetValue(roof, "thumb_root")
	filename := path.Join(thumb_root, "orig", e.Path)
	err = SaveFile(filename, data)
	if err != nil {
		return
	}

	err = mw.Ready(e)
	if err != nil {
		return
	}
	e.ready = 1

	e.Done = make(chan bool, 1)
	go func() {
		err = e._save(roof)
		if err != nil {
			log.Printf("_save error: %s", err)
		}
		e.Done <- true
	}()

	log.Printf("[%s] store ready ok %s", roof, e.Path)

	return
}

func (e *Entry) _save(roof string) (err error) {
	en := config.GetValue(roof, "engine")
	log.Printf("start save to engine %s", en)

	e.sev, err = PushBlob(e, roof)
	if err != nil {
		log.Printf("engine push error: %s", err)
		return
	}
	log.Print("engine push ok")

	mw := NewMetaWrapper(roof)
	if err = mw.SetDone(*e.Id, e.sev); err != nil {
		log.Println(err)
		// if err = mw.Save(e); err != nil {
		// 	return
		// }
		return
	}
	e.ready = -1
	log.Print("meta set done ok")
	return
}

func (e *Entry) fill(data []byte) error {
	if size := len(data); size != int(e.Size) {
		return fmt.Errorf("invliad size: %d (%d)", size, e.Size)
	}

	_, m := HashContent(data)

	if !e.Hashes.Contains(m) {
		return fmt.Errorf("invalid hash: %s (%s)", m, e.Hashes)
	}

	e.b = data
	return nil
}

func (e *Entry) reset() {
	e.b = []byte{}
}

func (e *Entry) origName() string {
	thumb_root := config.GetValue(e.roof(), "thumb_root")
	return path.Join(thumb_root, "orig", e.Path)
}

func (e *Entry) roof() string {
	if len(e.Roofs) > 0 {
		return e.Roofs[0].(string)
	}
	return ""
}

func newPath(ei *EntryId, ext string) string {
	r := ei.id
	p := r[0:2] + "/" + r[2:4] + "/" + r[4:] + ext

	return p
}

func PullBlob(e *Entry, roof string) (data []byte, err error) {
	var em backend.Wagoner
	em, err = backend.FarmEngine(roof)
	if err != nil {
		log.Printf("FarmEngine(%s) error: %s", roof, err)
		return
	}
	// var data []byte
	data, err = em.Get(e.Path)
	if err != nil {
		log.Printf("[%s] engine Get(%s) error: %s", roof, e.Path, err)
	}
	return
}

func PushBlob(e *Entry, roof string) (sev cdb.Hstore, err error) {
	var em backend.Wagoner
	em, err = backend.FarmEngine(roof)
	if err != nil {
		log.Printf("farm engine error: %s", err)
		return
	}
	sev, err = em.Put(e.Path, e.Blob(), e.Meta.Hstore())
	return
}
<commit_msg>simply newPath()<commit_after>// imsto core objects
package storage

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"path"
	"time"
	"wpst.me/calf/config"
	cdb "wpst.me/calf/db"
	iimg "wpst.me/calf/image"
	"wpst.me/calf/storage/backend"
)

type AppId uint16

type Author uint32

type Entry struct {
	Id       *EntryId   `json:"id,omitempty"`
	Name     string     `json:"name,omitempty"`
	Size     uint32     `json:"size"`
	Path     string     `json:"path"`
	Mime     string     `json:"mime,omitempty"`
	Status   uint8      `json:"-"`
	Hashes   cdb.Qarray `json:"-"`
	Ids      cdb.Qarray `json:"-"`
	Roofs    cdb.Qarray `json:"roofs"`
	Tags     cdb.Qarray `json:"tags,omitempty"`
	Meta     *iimg.Attr `json:"meta,omitempty"`
	AppId    AppId      `json:"appid,omitempty"`
	Author   Author     `json:"author,omitempty"`
	Modified uint64     `json:"modified,omitempty"`
	Created  time.Time  `json:"created,omitempty"`
	exif     cdb.Hstore
	sev      cdb.Hstore
	b        []byte
	h        string
	_treked  bool
	ret      int       // db saved result
	Done     chan bool `json:"-"`
	ready    int
}

const (
	min_size = 43
)

func NewEntry(data []byte, name string) (e *Entry, err error) {
	if len(data) < min_size {
		err = errors.New("data is too small, maybe not a valid image")
		return
	}

	var id *EntryId
	id, err = NewEntryIdFromData(data)

	if err != nil {
		log.Println(err)
		return
	}

	e = &Entry{
		Id:      id,
		Name:    name,
		Size:    uint32(len(data)),
		Created: time.Now(),
		b:       data,
		h:       id.hash,
	}

	// entry = &Entry{Id: id, Name: name, Size: ia.Size, Meta: ia, Path: path, Mime: mimetype, Hashes: hashes, Ids: ids}

	return
}

// 处理图片信息并填充
func (e *Entry) Trek(roof string) (err error) {
	if e._treked {
		return
	}
	e._treked = true
	var im iimg.Image
	rd := bytes.NewReader(e.b)
	im, err = iimg.Open(rd)

	if err != nil {
		log.Printf("image open error: %s", err)
		return
	}

	defer im.Close()

	ia := im.GetAttr()
	// log.Println(ia)

	max_quality := iimg.Quality(config.GetInt(roof, "max_quality"))
	if ia.Quality > max_quality {
		log.Printf("jpeg quality %d is too high, set to %d", ia.Quality, max_quality)
	} else {
		max_quality = ia.Quality
		log.Printf("jpeg quality %d is too low", ia.Quality)
	}
	im.SetOption(iimg.WriteOption{Quality: max_quality, StripAll: true})

	max_width := iimg.Dimension(config.GetInt(roof, "max_width"))
	max_height := iimg.Dimension(config.GetInt(roof, "max_height"))
	if ia.Width > max_width || ia.Height > max_height {
		err = fmt.Errorf("dimension %dx%d of %s is too big", ia.Width, ia.Height, e.Name)
		return
	}

	min_width := iimg.Dimension(config.GetInt(roof, "min_width"))
	min_height := iimg.Dimension(config.GetInt(roof, "min_height"))
	if ia.Width < min_width || ia.Height < min_height {
		err = fmt.Errorf("dimension %dx%d of %s is too small", ia.Width, ia.Height, e.Name)
		return
	}

	var data []byte
	data, err = im.GetBlob() // tack new data

	if err != nil {
		log.Printf("GetBlob error: %s", err)
		return
	}

	hashes := cdb.Qarray{e.h}
	ids := cdb.Qarray{e.Id.String()}

	size := len(data)
	if max_file_size := config.GetInt(roof, "max_file_size"); size > max_file_size {
		err = fmt.Errorf("file: %s size %d is too big, max is %d", e.Name, size, max_file_size)
		return
	}

	var id2 *EntryId
	id2, err = NewEntryIdFromData(data)
	if id2.hash != e.h {
		hashes = append(hashes, id2.hash)
		if err != nil {
			// log.Println(err)
			return
		}
		ids = append(ids, id2.id)
		e.Id = id2 // 使用新的 Id 作为主键
		e.h = id2.hash
		e.b = data
		e.Size = uint32(size)
	}

	ia.Size = iimg.Size(size) // 更新后的大小
	ia.Name = e.Name

	path := newPath(e.Id, ia.Ext)

	log.Printf("ext: %s, mime: %s\n", ia.Ext, ia.Mime)

	e.Meta = ia
	e.Path = path
	e.Mime = ia.Mime
	e.Hashes = hashes
	e.Ids = ids
	return
}

// return hash value string
// func (e *Entry) Hashed() string {
// 	return e.h
// }

// return binary bytes
func (e *Entry) Blob() []byte {
	return e.b
}

func (e *Entry) IsDone() bool {
	return e.ready != 1
}

func (e *Entry) Store(roof string) (err error) {

	mw := NewMetaWrapper(roof)
	eh, _err := mw.GetHash(e.h)
	if _err != nil { // ok, not exsits
		log.Printf("check hash error: %s", _err)
	} else if eh != nil && eh.id != "" {
		if _id, _err := NewEntryId(eh.id); _err == nil {
			e.Id = _id
			_ne, _err := mw.GetEntry(*_id)
			if _err == nil { // path, mime, size, sev, status, created
				if _ne.Roofs.Contains(roof) {
					e.Name = _ne.Name
					e.Path = _ne.Path
					e.Size = _ne.Size
					e.Mime = _ne.Mime
					e.Meta = _ne.Meta
					// e.sev = _ne.sev
					e.Created = _ne.Created
					e.Roofs = _ne.Roofs
					e.reset()
					e._treked = true

					log.Printf("exist: %s, %s", e.Id, e.Path)
					return
				}

				log.Printf("[%s]%s not in %s, so resubmit it", roof, e.Id, _ne.Roofs)

				// for _, _roof := range _ne.Roofs {
				// 	_mw := NewMetaWrapper(fmt.Sprint(_roof))
				// 	t, te := _mw.GetMeta(*_ne.Id)
				// 	if te == nil {
				// 		e = t
				// 		err = mw.Save(t)
				// 		return
				// 	}
				// }

				// e.Done <- true
			} else {
				log.Printf("get entry error: %s", _err)
			}
		}
	}

	if err = e.Trek(roof); err != nil {
		return
	}
	log.Printf("new id: %v, size: %d, path: %v\n", e.Id, e.Size, e.Path)

	data := e.Blob()
	// size := len(data)
	// log.Printf("blob length: %d", size)
	thumb_root := config.GetValue(roof, "thumb_root")
	filename := path.Join(thumb_root, "orig", e.Path)
	err = SaveFile(filename, data)
	if err != nil {
		return
	}

	err = mw.Ready(e)
	if err != nil {
		return
	}
	e.ready = 1

	e.Done = make(chan bool, 1)
	go func() {
		err = e._save(roof)
		if err != nil {
			log.Printf("_save error: %s", err)
		}
		e.Done <- true
	}()

	log.Printf("[%s] store ready ok %s", roof, e.Path)

	return
}

func (e *Entry) _save(roof string) (err error) {
	en := config.GetValue(roof, "engine")
	log.Printf("start save to engine %s", en)

	e.sev, err = PushBlob(e, roof)
	if err != nil {
		log.Printf("engine push error: %s", err)
		return
	}
	log.Print("engine push ok")

	mw := NewMetaWrapper(roof)
	if err = mw.SetDone(*e.Id, e.sev); err != nil {
		log.Println(err)
		// if err = mw.Save(e); err != nil {
		// 	return
		// }
		return
	}
	e.ready = -1
	log.Print("meta set done ok")
	return
}

func (e *Entry) fill(data []byte) error {
	if size := len(data); size != int(e.Size) {
		return fmt.Errorf("invliad size: %d (%d)", size, e.Size)
	}

	_, m := HashContent(data)

	if !e.Hashes.Contains(m) {
		return fmt.Errorf("invalid hash: %s (%s)", m, e.Hashes)
	}

	e.b = data
	return nil
}

func (e *Entry) reset() {
	e.b = []byte{}
}

func (e *Entry) origName() string {
	thumb_root := config.GetValue(e.roof(), "thumb_root")
	return path.Join(thumb_root, "orig", e.Path)
}

func (e *Entry) roof() string {
	if len(e.Roofs) > 0 {
		return e.Roofs[0].(string)
	}
	return ""
}

func newPath(ei *EntryId, ext string) string {
	return ei.id + ext
	// r := ei.id
	// p := r[0:2] + "/" + r[2:4] + "/" + r[4:] + ext

	// return p
}

func PullBlob(e *Entry, roof string) (data []byte, err error) {
	var em backend.Wagoner
	em, err = backend.FarmEngine(roof)
	if err != nil {
		log.Printf("FarmEngine(%s) error: %s", roof, err)
		return
	}
	// var data []byte
	data, err = em.Get(e.Path)
	if err != nil {
		log.Printf("[%s] engine Get(%s) error: %s", roof, e.Path, err)
	}
	return
}

func PushBlob(e *Entry, roof string) (sev cdb.Hstore, err error) {
	var em backend.Wagoner
	em, err = backend.FarmEngine(roof)
	if err != nil {
		log.Printf("farm engine error: %s", err)
		return
	}
	sev, err = em.Put(e.Path, e.Blob(), e.Meta.Hstore())
	return
}
<|endoftext|>


<commit_before>package hostqueue

import (
	"encoding/json"
	"net/http"
	"regexp"
	"strings"

	"appengine"

	"github.com/gorilla/mux"
)

//group/status/{groupUUID}
//Respond with the group name, current queue, who is up next in that queue, and if this week has a host yet.
func DisplayGroupStatus(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	pathVars := mux.Vars(r)
	AddUuid(c)

	uuid := pathVars["uuid"]
	c.Infof("URI", strings.Split(r.URL.Path, "/"))
	c.Infof("UUID", uuid)

	if isValidUUID((uuid)) {
		group, err := GetGroupByUUID(c, uuid)

		if err != nil {
			//c.Infof()
			w.Write([]byte("invalid group"))
		}

		jsonGroup, err := json.Marshal(group)
		if err != nil {
			panic(err)
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(jsonGroup)
	}

	w.Write([]byte("invalid group"))
}

func isValidUUID(text string) bool {
	r := regexp.MustCompile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
	return r.MatchString(text)
}
<commit_msg>added gorilla mux to handle path variables.<commit_after>package hostqueue

import (
	"encoding/json"
	"net/http"
	"regexp"
	"strings"

	"appengine"

	"github.com/gorilla/mux"
)

//group/status/{groupUUID}
//Respond with the group name, current queue, who is up next in that queue, and if this week has a host yet.
func DisplayGroupStatus(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	pathVars := mux.Vars(r)
	AddUuid(c)

	uuid := pathVars["uuid"]
	c.Infof("UUID: %s", uuid)

	if isValidUUID((uuid)) {
		group, err := GetGroupByUUID(c, uuid)
		c.Infof("group: %v", group)
		if err != nil {
			//c.Infof()
			w.Write([]byte("invalid group"))
		}

		jsonGroup, err := json.Marshal(group)
		if err != nil {
			panic(err)
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(jsonGroup)
	}

	w.Write([]byte("invalid group"))
}

func isValidUUID(text string) bool {
	r := regexp.MustCompile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
	return r.MatchString(text)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

var (
	addr = flag.String("a", ":9625", "Address to listen on")
)

var pageTmpl = template.Must(template.New("HTML").Parse(
	`

		<meta name="go-import" content="{{.Host}}{{.Path}} {{.Vcs}} {{.Url}}">

`))

type Source struct {
	Vcs, Url string
}

type Router map[string]Source

type Server struct {
	config string
	Routes chan Router
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var match struct {
		Source
		Host string
		Path string
	}
	if r.Method != "GET" {
		http.Error(w, "Method Not Allowed", 405)
		return
	}
	routes := <-s.Routes

	if src, root, ok := routes.findPath(r.URL.Path); !ok {
		http.Error(w, "Not Found", 404)
		return
	} else {
		match.Source = src
		match.Host = r.Host
		match.Path = root
	}
	if r.FormValue("go-get") != "1" {
		// if this request is not coming from the go tool, redirect
		// to godoc.org
		http.Redirect(w, r, "http://godoc.org/"+r.Host+r.URL.Path, http.StatusSeeOther)
		return
	}
	if err := pageTmpl.Execute(w, match); err != nil {
		http.Error(w, "Internal Server Error", 500)
	}
}

func (r Router) findPath(path string) (Source, string, bool) {
	nodes := strings.Split(path, "/")
	for len(nodes) > 0 {
		path := strings.Join(nodes, "/")
		if src, ok := r[path]; ok {
			return src, path, true
		}
		nodes = nodes[:len(nodes)-1]
	}
	return Source{}, "", false
}

func init() {
	log.SetFlags(0)
	log.SetPrefix("")
	flag.Usage = func() {
		log.Printf("Usage: %s [-a addr] config\n", os.Args[0])
		os.Exit(2)
	}
}

func main() {
	flag.Parse()

	if len(flag.Args()) != 1 {
		flag.Usage()
	}

	s := NewServer(flag.Arg(0))
	srv := &http.Server{
		Handler: s,
		Addr:    *addr,
	}
	go srv.ListenAndServe()
	log.Print("Listening on ", *addr)
	if err := s.Run(); err != nil {
		log.Fatal(err)
	}
}

func NewServer(config string) *Server {
	return &Server{
		config: config,
		Routes: make(chan Router),
	}
}

func (srv *Server) Run() error {
	r, err := NewRouter(srv.config)
	if err != nil {
		return err
	}
	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGHUP, os.Interrupt, os.Kill)

	for {
		select {
		case srv.Routes <- r:
		case s := <-sig:
			if s == os.Interrupt || s == os.Kill {
				return fmt.Errorf("Interrupt")
			}
			nr, err := NewRouter(srv.config)
			if err != nil {
				log.Print(err)
			}
			r = nr
		}
	}
}

func NewRouter(filename string) (Router, error) {
	var n int
	r := make(Router)

	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	buf := bufio.NewReader(file)
	for {
		line, err := buf.ReadString('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		n++
		if len(line) < 2 {
			continue
		}
		if line[0] == '#' {
			continue
		}
		fields := strings.Fields(line)
		if len(fields) != 3 {
			return nil, fmt.Errorf("%s:%d: too many fields in `%s'", filename, n, line)
			continue
		}
		if _, ok := r[fields[0]]; ok {
			return nil, fmt.Errorf("%s:%d: duplicate entry %s", filename, n, fields[0])
		}
		r[fields[0]] = Source{fields[1], fields[2]}
	}
	return r, nil
}
<commit_msg>use bufio.Scanner, general code cleanup<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

var (
	addr = flag.String("a", ":9625", "Address to listen on")
)

var pageTmpl = template.Must(template.New("HTML").Parse(
	`

		<meta name="go-import" content="{{.Host}}{{.Path}} {{.Vcs}} {{.Url}}">

`))

type Source struct {
	Vcs, Url string
}

type Router map[string]Source

type Server struct {
	config string
	Routes chan Router
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var match struct {
		Source
		Host string
		Path string
	}
	if r.Method != "GET" {
		http.Error(w, "Method Not Allowed", 405)
		return
	}
	routes := <-s.Routes

	if src, root, ok := routes.findPath(r.URL.Path); !ok {
		http.Error(w, "Not Found", 404)
		return
	} else {
		match.Source = src
		match.Host = r.Host
		match.Path = root
	}
	if r.FormValue("go-get") != "1" {
		// if this request is not coming from the go tool, redirect
		// to godoc.org
		http.Redirect(w, r, "http://godoc.org/"+r.Host+r.URL.Path, http.StatusSeeOther)
		return
	}
	if err := pageTmpl.Execute(w, match); err != nil {
		log.Print(err)
	}
}

func (r Router) findPath(path string) (Source, string, bool) {
	nodes := strings.Split(path, "/")
	for len(nodes) > 0 {
		path := strings.Join(nodes, "/")
		if src, ok := r[path]; ok {
			return src, path, true
		}
		nodes = nodes[:len(nodes)-1]
	}
	return Source{}, "", false
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("")
	flag.Usage = func() {
		log.Printf("Usage: %s [-a addr] config\n", os.Args[0])
		os.Exit(2)
	}
	flag.Parse()

	if len(flag.Args()) != 1 {
		flag.Usage()
	}

	s := NewServer(flag.Arg(0))
	srv := &http.Server{
		Handler: s,
		Addr:    *addr,
	}
	go srv.ListenAndServe()
	log.Print("Listening on ", *addr)
	if err := s.loadConfig(); err != nil {
		log.Fatal(err)
	}
}

func NewServer(config string) *Server {
	return &Server{
		config: config,
		Routes: make(chan Router),
	}
}

// runs in its own goroutine.
func (srv *Server) loadConfig() error {
	r, err := NewRouter(srv.config)
	if err != nil {
		return err
	}
	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGHUP)

	for {
		select {
		case srv.Routes <- r:
		case <-sig:
			if nr, err := NewRouter(srv.config); err != nil {
				log.Print(err)
			} else {
				r = nr
			}
		}
	}
}

func NewRouter(filename string) (Router, error) {
	r := make(Router)

	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	n := 0
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		n++
		if strings.HasPrefix(scanner.Text(), "#") {
			continue
		}
		fields := strings.Fields(scanner.Text())
		if len(fields) != 3 {
			return nil, fmt.Errorf("%s:%d: (%d) fields (expected 3) in %q",
				filename, n, len(fields), scanner.Text())
		}
		if _, ok := r[fields[0]]; ok {
			return nil, fmt.Errorf("%s:%d: duplicate entry %s", filename, n, fields[0])
		}
		r[fields[0]] = Source{fields[1], fields[2]}
	}
	return r, scanner.Err()
}
<|endoftext|>