Example #1
0
File: main.go Project: reusee/pv
func main() {
	root := "."
	args := os.Args
	if len(os.Args) > 1 {
		root = os.Args[1]
		args = args[1:]
	}
	var err error
	root, err = filepath.Abs(root)
	if err != nil {
		log.Fatalf("invalid path %v", err)
	}

	var newOnly bool
	var random bool
	for _, arg := range args {
		switch arg {
		case "new":
			newOnly = true
		case "random":
			random = true
		}
	}

	var images []string
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			fmt.Printf("%s %v\n", path, err)
			return nil
		}
		if info.IsDir() {
			return nil
		}
		what := mime.TypeByExtension(filepath.Ext(path))
		if !strings.HasPrefix(what, "image/") {
			return nil
		}
		images = append(images, path)
		return nil
	})
	if len(images) == 0 {
		p("no image.\n")
		return
	}

	sort.Sort(sort.Reverse(sort.StringSlice(images)))
	if random {
		for i := len(images) - 1; i >= 1; i-- {
			j := rand.Intn(i + 1)
			images[i], images[j] = images[j], images[i]
		}
	}

	data := &struct {
		Count map[string]int
	}{
		Count: make(map[string]int),
	}

	dbPath := filepath.Join(root, ".picture_viewer.json")
	file, err := jsonfile.New(&data, dbPath, 51294)
	if err != nil {
		log.Fatalf("open data file %v", err)
	}
	defer file.Save()

	var filtered []string
	for _, img := range images {
		if newOnly {
			if data.Count[img] > 0 {
				continue
			}
		}
		filtered = append(filtered, img)
	}
	images = filtered

	keys := make(chan rune)
	var nextImage func()
	g, err := lgtk.New(`
GdkPixbuf = lgi.GdkPixbuf

win = Gtk.Window{
	Gtk.Grid{
		expand = true,
		orientation = 'VERTICAL',
		Gtk.Label{
			id = 'filename',
		},
		Gtk.Button{
			label = 'Next',
			on_clicked = function() next_image() end,
		},
		Gtk.ScrolledWindow{
			id = 'scroll',
			Gtk.Image{
				id = 'img',
				expand = true,
			},
			expand = true,
		},
		Gtk.Button{
			label = 'Next',
			on_clicked = function() next_image() end,
		},
	},
}
win.on_destroy:connect(Gtk.main_quit)
win.on_key_press_event:connect(function(_, ev)
	key_press(ev.keyval)
	return true
end)

win:show_all()
	`,
		"key_press", func(k rune) {
			select {
			case keys <- k:
			default:
			}
		},
		"next_image", func() {
			nextImage()
		})
	if err != nil {
		log.Fatal(err)
	}
	defer g.Close()

	index := 0
	showImage := func() {
		g.ExecEval(`
print(F)
buf, err = GdkPixbuf.Pixbuf.new_from_file(F)
win.child.img:set_from_pixbuf(buf)
win.child.scroll.vadjustment:set_value(0)
win.child.filename:set_label(F)
`,
			"F", images[index])
	}
	showImage()

	nextImage = func() {
		if index == len(images)-1 {
			return
		}
		data.Count[images[index]]++
		index++
		showImage()
	}

loop:
	for key := range keys {
		switch key {
		case 'q':
			break loop
		case ' ':
			nextImage()
			time.Sleep(time.Millisecond * 500)
		case 'z':
			if index == 0 {
				continue loop
			}
			index--
			showImage()
		}
	}
}
Example #2
0
func (db *Db) lgi_gst(infos []*PathInfo) {
	index := 0
	keys := make(chan rune)
	inputChan := make(chan string)

	g, err := lgtk.New(`
Gst = lgi.require('Gst', '1.0')
GstVideo = lgi.require('GstVideo', '1.0')
GdkX11 = lgi.GdkX11

win = Gtk.Window{
	Gtk.Grid{
		expand = true,
		orientation = 'VERTICAL',
		Gtk.DrawingArea{
			id = 'output',
			expand = true,
			can_focus = true,
		},
		Gtk.Label{
			id = 'uri',
		},
		Gtk.Entry{
			id = 'input',
			visible = false,
		},
	},
}
function win.on_destroy() Exit() end
function win.on_realize()
	win.child.input:hide()
end

function win:on_key_press_event(event)
	Key(event.keyval)
	return true
end

pipeline = Gst.ElementFactory.make('playbin', 'bin')
sink = Gst.ElementFactory.make('ximagesink', 'sink')
pipeline.video_sink = sink

xid = true
function reload_video()
	pipeline.state = 'NULL'
	sink:set_window_handle(xid)
	path = GetPath()
	print('playing', path)
	pipeline.uri = 'file://' .. path
	pipeline.state = 'PLAYING'
	win.child.uri.label = path
end
function win.child.output:on_realize()
	xid = self.window:get_xid()
	reload_video()
end

function seek(position, duration, flag)
	if position > duration then
		return
	end
	if position < 0 then
		position = 0
	end
	pipeline:seek_simple(Gst.Format.TIME, {'FLUSH', 'KEY_UNIT', flag}, position)
end
function seek_abs(position)
	pipeline:seek_simple(Gst.Format.TIME, {'FLUSH', 'KEY_UNIT', 'SNAP_BEFORE'}, position)
end
function seek_time(n)
	position = pipeline:query_position('TIME')
	if position == nil then return end
	position = position + n
	duration = pipeline:query_duration('TIME')
	flag = 'SNAP_AFTER'
	if n < 0 then flag = 'SNAP_BEFORE' end
	seek(position, duration, flag)
end
function seek_percent(n)
	position = pipeline:query_position('TIME')
	if position == nil then return end
	duration = pipeline:query_duration('TIME')
	position = position + duration / 100 * n
	flag = 'SNAP_AFTER'
	if n < 0 then flag = 'SNAP_BEFORE' end
	seek(position, duration, flag)
end

paused = false
function toggle_pause()
	if paused then
		pipeline.state = 'PLAYING'
		paused = false
	else
		pipeline.state = 'PAUSED'
		paused = true
	end
end

pipeline.bus:add_watch(GLib.PRIORITY_DEFAULT, function(bus, message)
	if message.type.ERROR then
		print('Error:', message:parse_error().message, GetPath())
	end
	if message.type.EOS then
		print('end of stream')
		seek_abs(0)
		pipeline.state = 'PLAYING'
	end
	return true
end)

input = win.child.input
input.on_activate:connect(function()
	input:hide()
	win.child.output:grab_focus()
	ProvideInput(input:get_text())
	pipeline.state = 'PLAYING'
	return true
end)

win:show_all()
	`,
		"GetPath", func() string {
			return infos[index].path
		},
		"Key", func(val rune) {
			select {
			case keys <- val:
			default:
			}
		},
		"ProvideInput", func(s string) {
			inputChan <- s
		},
	)
	if err != nil {
		panic(err)
	}

	// helper functions
	getPos := func() int64 {
		var ret int64 = -1
		g.WaitExec(func() {
			pos, ok := g.Eval(`return pipeline:query_position('TIME')`)[0].(float64)
			if ok {
				ret = int64(pos)
			}
		})
		return ret
	}
	run := func(code string) {
		g.Exec(func() {
			g.Eval(code)
		})
	}

	/*
		// watch count
		resetTimer := make(chan bool)
		go func() {
			minWatchTime := time.Second * 30
			t := time.NewTimer(minWatchTime)
			for {
				select {
				case <-t.C: // watched
					infos[index].file.WatchCount++
					t.Stop()
					p("watched %s\n", infos[index].path)
					db.Save()
				case <-resetTimer:
					t.Reset(minWatchTime)
				}
			}
		}()
	*/

	reload := func() {
		run("reload_video()")
		//resetTimer <- true
	}

	for {
		key := <-keys
		switch key {
		case 'q':
			os.Exit(0)

		case ' ':
			// toggle pause
			run("toggle_pause()")

		case 'j', 'r':
			// next video
			infos[index].file.WatchCount++
			p("watched %s\n", infos[index].path)
			db.Save()
			index += 1
			if index >= len(infos) {
				index = 0
			}
			reload()
		case 'k', 'z':
			// prev video
			index -= 1
			if index < 0 {
				index = len(infos) - 1
			}
			reload()

		case 'd':
			// seek forward
			run("seek_time(3000000000)")
		case 'a':
			// seek backward
			run("seek_time(-3000000000)")
		case 's':
			// seek forward long
			run("seek_time(10000000000)")
		case 'w':
			// seek backward long
			run("seek_time(-10000000000)")
		case 'D':
			// seek percent forward
			run("seek_percent(3)")
		case 'A':
			// seek percent backward
			run("seek_percent(-3)")
		case 'S':
			// seek percent forward long
			run("seek_percent(10)")
		case 'W':
			// seek percent backward long
			run("seek_percent(-10)")

		case 'e':
			// tag
			pos := getPos()
			if pos < 0 {
				continue
			}
			run(`
				pipeline.state = 'PAUSED'
				input:show()
				input:grab_focus()
			`)
			desc := <-inputChan
			if desc != "" {
				p("add tag %d %s\n", pos, desc)
				infos[index].file.AddTag(pos, desc)
				db.Save()
			}
		case 'f':
			// next tag
			pos := getPos()
			if pos < 0 {
				continue
			}
			var next int64
			for _, tag := range infos[index].file.Tags {
				if tag.Position > pos {
					if tag.Position < next || next == 0 {
						next = tag.Position
					}
				}
			}
			if next > 0 {
				p("jump to tag %d\n", next)
				run(s("seek_abs(%d)", next))
			}
		case 'c':
			// prev tag
			pos := getPos()
			if pos < 0 {
				continue
			}
			var prev int64
			for _, tag := range infos[index].file.Tags {
				if tag.Position < pos {
					if tag.Position > prev || prev == 0 {
						prev = tag.Position
					}
				}
			}
			if prev > 0 {
				p("jump to tag %d\n", prev)
				run(s("seek_abs(%d)", prev))
			}
		case 'x':
			// to first tag
			if len(infos[index].file.Tags) > 0 {
				run(s("seek_abs(%d)", infos[index].file.Tags[0].Position))
			}

		}
	}

}
Example #3
0
func ui_gtk(connects []*Connect, mem *Memory) {
	// ui
	keys := make(chan rune)
	g, err := lgtk.New(`
Gdk = lgi.Gdk

css = Gtk.CssProvider.get_default()
css:load_from_data([[
GtkWindow {
	background-color: black;
	color: white;
}
#hint {
	font-size: 16px;
}
#text {
	font-size: 48px;
	color: #0099CC;
}
#level {
	color: grey;
}
]])
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), css, 999)

win = Gtk.Window{
	Gtk.Grid{
		orientation = 'VERTICAL',
		Gtk.Label{
			expand = true,
		},
		Gtk.Label{
			id = 'hint',
			name = 'hint',
		},
		Gtk.Label{
			id = 'text',
			name = 'text',
		},
		Gtk.Label{
			expand = true,
		},
		Gtk.Label{
			id = 'level',
			name = 'level',
		},
	},
}

function win:on_key_press_event(ev)
	Key(ev.keyval)
	return true
end
function win.on_destroy()
	Exit(0)
end
win:show_all()

	`,
		"Key", func(val rune) {
			select {
			case keys <- val:
			default:
			}
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	setHint := func(s string) {
		g.ExecEval(`win.child.hint:set_label(T)`, "T", s)
	}
	setText := func(s string) {
		g.ExecEval(`win.child.text:set_label(T)`, "T", s)
	}
	setLevel := func(s string) {
		g.ExecEval(`win.child.level:set_label(T)`, "T", s)
	}

	setHint("press f to start")
	for {
		key := <-keys
		if key == 'f' {
			break
		}
	}
	setHint("")

	wg := new(sync.WaitGroup)
	save := func() {
		wg.Add(1)
		go func() {
			mem.Save()
			wg.Done()
		}()
	}

	// train
loop:
	for _, connect := range connects {
		setHint("")
		setText("")

		from := mem.Concepts[connect.From]
		to := mem.Concepts[connect.To]
		lastHistory := connect.Histories[len(connect.Histories)-1]
		setLevel(strconv.Itoa(lastHistory.Level))

		switch from.What {

		case AUDIO: // play audio
			setHint("playing...")
			from.Play()
			if to.What == WORD {
				setHint("press any key to show answer")
				<-keys
				setText(to.Text)
			}
		repeat:
			setHint("press G to levelup, T to reset level, Space to repeat")
		read_key:
			key := <-keys
			switch key {
			case 'g':
				connect.Histories = append(connect.Histories, History{Level: lastHistory.Level + 1, Time: time.Now()})
				save()
			case 't':
				connect.Histories = append(connect.Histories, History{Level: 0, Time: time.Now()})
				save()
			case ' ':
				setHint("playing...")
				from.Play()
				setHint("")
				goto repeat
			case 'q':
				setText("")
				setHint("exit...")
				break loop
			default:
				goto read_key
			}

		case WORD: // show text
			setText(from.Text)
			setHint("press any key to play audio")
			<-keys
		repeat2:
			setHint("playing...")
			to.Play()
			setHint("press G to levelup, T to reset level, Space to repeat")
		read_key2:
			key := <-keys
			switch key {
			case 'g':
				connect.Histories = append(connect.Histories, History{Level: lastHistory.Level + 1, Time: time.Now()})
				save()
			case 't':
				connect.Histories = append(connect.Histories, History{Level: 0, Time: time.Now()})
				save()
			case ' ':
				goto repeat2
			case 'q':
				setText("")
				setHint("exit...")
				break loop
			default:
				goto read_key2
			}

		default:
			panic("impossible")
		}
	}

	wg.Wait()
}