Esempio n. 1
0
func extractFpUtil(archive []byte) {
	zipReader, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
	if err != nil {
		utils.Log(utils.ERROR, "recognizer.extractFpUtil: failed to unzip fingerprint util archive: %v", err)
		return
	}

	for _, file := range zipReader.File {
		if len(file.Name) < len(fpUtil()) || file.Name[len(file.Name)-len(fpUtil()):] != fpUtil() {
			continue
		}

		src, err := file.Open()
		if err != nil {
			utils.Log(utils.ERROR, "recognizer.extractFpUtil: failed to extract fingerprint util from archive: %v", err)
			return
		}
		defer src.Close()

		dst, err := os.OpenFile(pathToFpUtil(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
		if err != nil {
			utils.Log(utils.ERROR, "recognizer.extractFpUtil: failed to open file '%v' for writing: %v", pathToFpUtil(), err)
			return
		}
		defer dst.Close()

		if _, err := io.Copy(dst, src); err != nil {
			utils.Log(utils.ERROR, "recognizer.extractFpUtil: failed to save fingerprint util to '%v': %v", pathToFpUtil(), err)
			return
		}

		break
	}
}
Esempio n. 2
0
func extractFpUtil(archive []byte) {
	gzipReader, err := gzip.NewReader(bytes.NewReader(archive))
	if err != nil {
		utils.Log(utils.ERROR, "recognizer.extractFpUtil: failed to ungzip fingerprint util archive: %v", err)
		return
	}

	tarReader := tar.NewReader(gzipReader)

	for {
		header, err := tarReader.Next()
		if err == io.EOF || err != nil {
			break
		}
		if header.Typeflag != tar.TypeReg ||
			len(header.Name) < len(fpUtil()) ||
			header.Name[len(header.Name)-len(fpUtil()):] != fpUtil() {
			continue
		}

		dst, err := os.OpenFile(pathToFpUtil(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
		if err != nil {
			utils.Log(utils.ERROR, "recognizer.extractFpUtil: failed to open file '%v' for writing: %v", pathToFpUtil(), err)
			return
		}
		defer dst.Close()

		if _, err := io.Copy(dst, tarReader); err != nil {
			utils.Log(utils.ERROR, "recognizer.extractFpUtil: failed to save fingerprint util to '%v': %v", pathToFpUtil(), err)
			return
		}
	}
}
Esempio n. 3
0
func lookupByFingerPrint(fingetPrint string, duration int) (string, error) {
	data := "client=" + appKey + "&meta=releases+tracks+compress&duration=" + strconv.Itoa(duration) + "&fingerprint=" + fingetPrint
	var zippedData bytes.Buffer
	zipper := gzip.NewWriter(&zippedData)
	zipper.Write([]byte(data))
	zipper.Close()

	request, err := http.NewRequest("POST", "http://api.acoustid.org/v2/lookup", bytes.NewReader(zippedData.Bytes()))
	if err != nil {
		utils.Log(utils.ERROR, "Failed to make new http request: %v", err)
		return "", err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	request.Header.Add("Content-Encoding", "gzip")

	response, err := (&http.Client{}).Do(request)
	if err != nil {
		utils.Log(utils.ERROR, "Failed to send http request: %v", err)
		return "", err
	}

	reply, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if err != nil {
		utils.Log(utils.ERROR, "Failed to read http response: %v", err)
		return "", err
	}
	return string(reply), nil
}
Esempio n. 4
0
func (tagger *Tagger) processDir(src, dst string) error {
	utils.Log(utils.INFO, "Start processing directory '%v'", src)

	allFiles := getAllFiles(src)
	tagger.counter.setTotal(len(allFiles))
	utils.Log(utils.INFO, "Found %v files", len(allFiles))

	var result atomic.Value
	var index int32 = -1
	var wg sync.WaitGroup

	for i := 0; i < numberOfThreads; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for {
				if tagger.stop.Load().(bool) {
					utils.Log(utils.WARNING, "Processing directory '%v' interrupted by application stop", src)
					return
				}

				i := atomic.AddInt32(&index, 1)
				if i >= int32(len(allFiles)) {
					return
				}

				fmt.Printf("\rProcessing %v/%v", i, len(allFiles))
				destination, err := tagger.getDestinationPath(allFiles[i])
				if err != nil {
					result.Store(err)
					utils.Log(utils.ERROR, "Failed to get destination path: %v", err)
					continue
				}
				if err := tagger.processFile(allFiles[i], destination); err != nil {
					utils.Log(utils.ERROR, "Failed to process file '%v': %v", allFiles[i], err)
					result.Store(err)
				}
			}
		}()
	}
	wg.Wait()
	fmt.Printf("\r                        \r")

	if result.Load() != nil {
		return result.Load().(error)
	}
	return nil
}
Esempio n. 5
0
func Recognize(path string, existingTag ...editor.Tag) (editor.Tag, error) {
	fingerPrint, duration, err := getFingerPrint(path)
	if err != nil {
		utils.Log(utils.ERROR, "Failed to get finger print for file '%v': %v", path, err)
		return editor.Tag{}, err
	}

	return askMusicBrainz(fingerPrint, duration, existingTag...)
}
Esempio n. 6
0
func urlToFpUtil() string {
	switch runtime.GOARCH {
	case "386":
		return "https://bitbucket.org/acoustid/chromaprint/downloads/chromaprint-fpcalc-" + fpUtilVersion + "-win-i686.zip"
	case "amd64":
		return "https://bitbucket.org/acoustid/chromaprint/downloads/chromaprint-fpcalc-" + fpUtilVersion + "-win-x86_64.zip"
	}

	utils.Log(utils.ERROR, "recognizer.urlToFpUtil: unsupported architecture: %v", runtime.GOARCH)
	return ""
}
Esempio n. 7
0
func askCoverArtArchive(releaseId string) editor.Cover {
	response, err := http.Get("http://coverartarchive.org/release/" + releaseId)
	if err != nil {
		utils.Log(utils.ERROR, "Failed to send http request '%v' and get response: %v", "http://coverartarchive.org/release/"+releaseId, err)
		return editor.Cover{}
	}
	if response.StatusCode != 200 {
		return editor.Cover{}
	}

	reply, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if err != nil {
		utils.Log(utils.ERROR, "Failed to read http response: %v", err)
		return editor.Cover{}
	}

	imageUrl := parseCoverArtArchiveReply(string(reply))
	return getCover(imageUrl)
}
Esempio n. 8
0
func (tagger *Tagger) trapSignal() {
	tagger.stop.Store(false)

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)

	go func() {
		_ = <-ch
		tagger.stop.Store(true)
		utils.Log(utils.INFO, "Got signal to stop")
	}()
}
Esempio n. 9
0
func getFpUtil() {
	// fingerprint util is here, do nothing
	if _, err := os.Stat(pathToFpUtil()); err == nil {
		return
	}

	// download fingerprint util
	url := urlToFpUtil()
	response, err := http.Get(url)
	if err != nil || response.StatusCode != 200 {
		utils.Log(utils.ERROR, "recognizer.getFpUtil: failed to download fingerprint util from '%v': %v", url, err)
		return
	}

	content, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if err != nil {
		utils.Log(utils.ERROR, "recognizer.getFpUtil: failed to read content of '%v': %v", url, err)
		return
	}

	// extract fingerprint util
	extractFpUtil(content)
}
Esempio n. 10
0
func askMusicBrainz(fingerPrint string, duration int, existingTag ...editor.Tag) (editor.Tag, error) {
	waitIfNeeded()

	reply, err := lookupByFingerPrint(fingerPrint, duration)
	if err != nil {
		utils.Log(utils.ERROR, "Failed to lookup by finger print: %v", err)
		return editor.Tag{}, err
	}

	tag, releaseId := parseAcousticIdReply(reply, existingTag...)
	if len(releaseId) > 0 {
		tag.Cover = askCoverArtArchive(releaseId)
	}

	return tag, nil
}
Esempio n. 11
0
func main() {
	utils.Log(utils.INFO, "Starting Go Music Tagger %v", version)
	if !parseCommandLineArguments() {
		printUsage()
		return
	}

	tagger, err := logic.NewTagger(source, destination, filter, useExistingTag)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}

	if err = tagger.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	tagger.PrintReport()
}
Esempio n. 12
0
func (tagger *Tagger) init(source, dest, filter string) error {
	utils.Log(utils.INFO, "Initializing tagger with source = '%v', destination = '%v', filter = '%v'", source, dest, filter)

	var err error
	tagger.source, err = filepath.Abs(source)
	if err != nil {
		utils.Log(utils.ERROR, "Failed to make source path '%v' absolute: %v", source, err)
		return err
	}

	tagger.sourceInfo, err = os.Stat(tagger.source)
	if err != nil {
		utils.Log(utils.ERROR, "Failed to get source path '%v' info: %v", tagger.source, err)
		return err
	}

	if !tagger.sourceInfo.IsDir() && !isSupportedFile(tagger.source) {
		return fmt.Errorf("Input file '%v' is unsupported", tagger.source)
	}

	if len(dest) == 0 {
		tagger.destination = tagger.source
	} else {
		tagger.destination, err = filepath.Abs(dest)
		if err != nil {
			utils.Log(utils.ERROR, "Failed to make destination path '%v' absolute: %v", dest, err)
			return err
		}

		destinationInfo, err := os.Stat(tagger.destination)
		if err != nil {
			// if input is file consider destination as path to non-existent file
			if !tagger.sourceInfo.IsDir() && !isSupportedFile(tagger.destination) {
				utils.Log(utils.ERROR, "Output file '%v' is unsupported", tagger.destination)
				return fmt.Errorf("Output file '%v' is unsupported", tagger.destination)
			}
			// if input is directory consider destination as non-existent directory and try to create it
			err = os.MkdirAll(tagger.destination, 0666)
			if err != nil {
				utils.Log(utils.ERROR, "Failed to create directory '%v': %v", tagger.destination, err)
				return err
			}
		} else {
			if tagger.sourceInfo.IsDir() && !destinationInfo.IsDir() {
				return fmt.Errorf("Cannot output directory '%v' into file '%v'", tagger.source, tagger.destination)
			}
			if !tagger.sourceInfo.IsDir() && destinationInfo.IsDir() {
				tagger.destination = filepath.Join(tagger.destination, filepath.Base(tagger.source))
			}
			if !destinationInfo.IsDir() && !isSupportedFile(tagger.destination) {
				return fmt.Errorf("Output file '%v' is unsupported", tagger.destination)
			}
		}
	}

	tagger.filter, err = filterStringToType(filter)
	if err != nil {
		return err
	}

	tagger.counter = &Counter{}
	return nil
}
Esempio n. 13
0
func (tagger *Tagger) processFile(src, dst string) error {
	utils.Log(utils.INFO, "Start processing file '%v'", src)

	tagEditor := makeEditor(src)
	tag, err := tagEditor.ReadTag(src)
	if err != nil {
		tagger.counter.addFail()
		utils.Log(utils.ERROR, "Failed to read tags from file '%v': %v", src, err)
		return err
	}

	if !tagger.filterByTag(tag) {
		utils.Log(utils.INFO, "Update tag is not required for file '%v'", src)
		return nil
	}
	tagger.counter.addFiltered()

	if tagger.stop.Load().(bool) {
		utils.Log(utils.WARNING, "Processing file '%v' interrupted by application stop", src)
		return fmt.Errorf("Processing file '%v' interrupted by application stop", src)
	}

	var newTag editor.Tag
	if tagger.useExistingTag {
		newTag, err = recognizer.Recognize(src, tag)
	} else {
		newTag, err = recognizer.Recognize(src)
	}
	if err != nil {
		tagger.counter.addFail()
		utils.Log(utils.ERROR, "Failed to recognize composition from file '%v': %v", src, err)
		return err
	}

	if newTag.Empty() {
		tagger.counter.addFail()
		utils.Log(utils.WARNING, "Composition from file '%v' is not recognized", src)
		return nil
	}

	// if we need only cover and there is no cover, return here
	if tagger.filter == NoCover && newTag.Cover.Empty() {
		tagger.counter.addFail()
		utils.Log(utils.WARNING, "Cover for file '%v' is not found", src)
		return nil
	}

	// if we need only cover and already has smth else, take only cover
	if tagger.filter == NoCover && !tag.Empty() {
		tag.Cover = newTag.Cover
		newTag = tag
	} else {
		newTag.MergeWith(tag)
	}

	err = tagger.preparePath(dst)
	if err != nil {
		tagger.counter.addFail()
		utils.Log(utils.ERROR, "Failed to prepare path '%v': %v", dst, err)
		return err
	}

	err = tagEditor.WriteTag(src, dst, newTag)
	if err != nil {
		tagger.counter.addFail()
		utils.Log(utils.ERROR, "Failed to write tag and save file '%v': %v", dst, err)
		return err
	}

	tagger.counter.addSuccess(!newTag.Cover.Empty())
	utils.Log(utils.INFO, "File '%v' successfully processed, cover found: %v", src, newTag.Cover.Empty())
	return nil
}