diff --git a/importer/fetch_converter_utils.go b/importer/fetch_converter_utils.go
index b63fca012be7cc00194ab29d1231f0cc91c8ea84..642c339c21c314ece5370f8bbb453938b85cc884 100644
--- a/importer/fetch_converter_utils.go
+++ b/importer/fetch_converter_utils.go
@@ -81,3 +81,29 @@ func (l *convLogger) run() {
 func (l *convLogger) Wait() {
 	<-l.done
 }
+
+// this is not a perfect io.Reader:
+//  - it will only return one line on each invocation even if len(p) would allow us to
+//    return more than one line
+//  - lines longer than len(p) will be truncated
+type JobLogReader struct {
+	log    JobLog
+	pos    int
+	stream string
+}
+
+func NewJobLogReader(log JobLog, stream string) *JobLogReader {
+	return &JobLogReader{log, -1, stream}
+}
+
+func (r *JobLogReader) Read(p []byte) (n int, err error) {
+	for {
+		r.pos++
+		if r.pos >= len(r.log) {
+			return 0, io.EOF
+		}
+		if r.stream == "" || r.log[r.pos].Stream == r.stream {
+			return copy(p, []byte(r.log[r.pos].Line)), nil
+		}
+	}
+}
diff --git a/importer/fetch_converter_utils_test.go b/importer/fetch_converter_utils_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e38cc6ae2840eb55e04ac40adea45ec80542aa13
--- /dev/null
+++ b/importer/fetch_converter_utils_test.go
@@ -0,0 +1,102 @@
+//
+//  tank
+//
+//  Import and Playlist Daemon for autoradio project
+//
+//
+//  Copyright (C) 2017-2018 Christian Pointner <equinox@helsinki.at>
+//
+//  This file is part of tank.
+//
+//  tank is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  any later version.
+//
+//  tank is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with tank. If not, see <http://www.gnu.org/licenses/>.
+//
+
+package importer
+
+import (
+	"io"
+	"testing"
+)
+
+func TestJobLogReader(t *testing.T) {
+	var log JobLog
+	var buf [1024]byte
+
+	r := NewJobLogReader(log, "")
+	p := buf[:]
+	n, err := r.Read(p)
+	if n != 0 || err != io.EOF {
+		t.Fatalf("an empty log should yield %d, %v but got: %d, %v", 0, io.EOF, n, err)
+	}
+
+	log.append("stdout", "this is a test")
+	log.append("stdout", "this is another test")
+	log.append("stderr", "oh no something went wrong!")
+
+	r = NewJobLogReader(log, "")
+	for i, l := range log {
+		p = buf[:]
+		n, err = r.Read(p)
+		if n != len(l.Line) || err != nil {
+			t.Fatalf("reading line %d failed, expected return code %d, %v but got: %d, %v", i+1, len(l.Line), nil, n, err)
+		}
+		line := string(p[:n])
+		if l.Line != line {
+			t.Fatalf("reading line %d failed, expected line %q but got: %q", i+1, l.Line, line)
+		}
+	}
+	if n, err = r.Read(p); n != 0 || err != io.EOF {
+		t.Fatalf("reading after the end of log should yield %d, %v but got: %d, %v", 0, io.EOF, n, err)
+	}
+
+	r = NewJobLogReader(log, "stderr")
+	for i, l := range log {
+		if l.Stream != "stderr" {
+			continue
+		}
+
+		p = buf[:]
+		n, err = r.Read(p)
+		if n != len(l.Line) || err != nil {
+			t.Fatalf("reading line %d failed, expected return code %d, %v but got: %d, %v", i+1, len(l.Line), nil, n, err)
+		}
+		line := string(p[:n])
+		if l.Line != line {
+			t.Fatalf("reading line %d failed, expected line %q but got: %q", i+1, l.Line, line)
+		}
+	}
+	if n, err = r.Read(p); n != 0 || err != io.EOF {
+		t.Fatalf("reading after the end of log should yield %d, %v but got: %d, %v", 0, io.EOF, n, err)
+	}
+
+	r = NewJobLogReader(log, "stdout")
+	for i, l := range log {
+		if l.Stream != "stdout" {
+			continue
+		}
+
+		p = buf[:]
+		n, err = r.Read(p)
+		if n != len(l.Line) || err != nil {
+			t.Fatalf("reading line %d failed, expected return code %d, %v but got: %d, %v", i+1, len(l.Line), nil, n, err)
+		}
+		line := string(p[:n])
+		if l.Line != line {
+			t.Fatalf("reading line %d failed, expected line %q but got: %q", i+1, l.Line, line)
+		}
+	}
+	if n, err = r.Read(p); n != 0 || err != io.EOF {
+		t.Fatalf("reading after the end of log should yield %d, %v but got: %d, %v", 0, io.EOF, n, err)
+	}
+}