func (d *Directory) resetCold() {
	for _, e := range d.Items {
		e.cold = true
	}
	for _, s := range d.Subdirs {
		s.resetCold()
	}
}
resetCold tags all entries as cold before reenumerating the directory.
package main
import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/maruel/interrupt"
	"github.com/maruel/serve-mp4/vid"
	fsnotify "gopkg.in/fsnotify.v1"
)
type Entry struct {
	Rel           string  
	preferredLang string  
	rootDir       string  
	mu          sync.Mutex
	info        *vid.Info
	err         error                
	cached      map[vid.Device]bool  
	transcoding bool                 
	frame       int                  
	cold        bool                 
}
func (e *Entry) IsCached(v vid.Device) bool {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.cached[v]
}
func (e *Entry) IsCachedChromeCast() bool {
	return e.IsCached(vid.ChromeCast)
}
func (e *Entry) IsCachedChromeOS() bool {
	return e.IsCached(vid.ChromeOS)
}
func (e *Entry) Path(v vid.Device) string {
	return e.Rel[:len(e.Rel)-len(filepath.Ext(e.Rel))+1] + v.ToContainer()
}
func (e *Entry) ChromeCastPath() string {
	return e.Path(vid.ChromeCast)
}
func (e *Entry) ChromeOSPath() string {
	return e.Path(vid.ChromeOS)
}
func (e *Entry) IsTranscoding() bool {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.transcoding
}
func (e *Entry) Percent() string {
	v := e.Info()
	if v == nil {
		return "N/A"
	}
	e.mu.Lock()
	f := e.frame
	e.mu.Unlock()
	nb, err := strconv.Atoi(v.Raw.Streams[v.VideoIndex].NbFrames)
	if err != nil {
		return "N/A"
	}
	return fmt.Sprintf("%3.1f%%", 100.*float32(f)/float32(nb))
}
func (e *Entry) TryInfo() *vid.Info {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.info
}
func (e *Entry) Info() *vid.Info {
	s := e.srcFile()
	e.mu.Lock()
	defer e.mu.Unlock()
	if e.info == nil && e.err == nil {
		if e.info, e.err = vid.Identify(s, e.preferredLang); e.err != nil {
			log.Printf("%q:%v", s, e.err)
		}
	}
	return e.info
}
func (e *Entry) srcFile() string {
	return filepath.Join(e.rootDir, e.Rel)
}
type Directory struct {
	Items   map[string]*Entry
	Subdirs map[string]*Directory
}
func (d *Directory) StillLoading() bool {
	for _, e := range d.Items {
		if e.IsTranscoding() {
			return true
		}
	}
	for _, s := range d.Subdirs {
		if s.StillLoading() {
			return true
		}
	}
	return false
}
func (d *Directory) TotalItems() int {
	t := len(d.Items)
	for _, s := range d.Subdirs {
		t += s.TotalItems()
	}
	return t
}
func (d *Directory) lookupDir(dir string) *Directory {
	if dir == "" {
		return d
	}
	i := strings.IndexByte(dir, '/')
	if i == -1 {
		return d.Subdirs[dir]
	}
	b := dir[:i]
	s := d.Subdirs[b]
	if s == nil {
		return nil
	}
	return s.lookupDir(dir[i+1:])
}
func (d *Directory) lookupEntry(rel string) *Entry {
	s := d
	if i := strings.LastIndexByte(rel, '/'); i != -1 {
		if i == 0 {
			return nil
		}
		s = d.lookupDir(rel[:i])
		if s == nil {
			return nil
		}
		rel = rel[i+1:]
	}
	return s.Items[rel]
}
func (d *Directory) findEntryToPreload() *Entry {
	for _, e := range d.Items {
		if e.info == nil && e.err == nil {
			return e
		}
	}
	for _, s := range d.Subdirs {
		if e := s.findEntryToPreload(); e != nil {
			return e
		}
	}
	return nil
}