Full Code of kalbhor/MusicRepair for AI

master 0865ce68d543 cached
7 files
11.4 KB
3.3k tokens
14 symbols
1 requests
Download .txt
Repository: kalbhor/MusicRepair
Branch: master
Commit: 0865ce68d543
Files: 7
Total size: 11.4 KB

Directory structure:
gitextract_xfdxu3u9/

├── .gitignore
├── LICENSE
├── README.md
├── main.go
├── repair.go
├── spotify.go
└── utils.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
#### joe made this: http://goel.io/joe

#### go ####
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# External packages folder
vendor/


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2016 Lakshay Kalbhor

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<p align="center">
  <b>Adds Metadata to Music files</b>
</p>

<p align="center">
    <a href="https://github.com/kalbhor/musicrepair/LICENSE">
		<img alt="License"  src="https://img.shields.io/github/license/mashape/apistatus.svg?style=flat-square"/>
	</a>        
	<a href="https://github.com/kalbhor/musicrepair">
		<img alt="stars" src="https://img.shields.io/github/stars/kalbhor/musicrepair.svg?style=social&label=Star"/>
	</a>            
</p>

## Features

1. Fixes songs in nested directories recursively.
2. Fetches metadata from [Spotify](https://www.spotify.com)
3. Multiple options to format file (Options to revert file back)
4. Simple binary

## Dependencies  

### [Spotify API](https://developer.spotify.com/my-applications)

1. Create an account and register an application.
2. Copy the Client ID and Client Secret.
3. Set them in *config file* after running ```musicrepair -config```

###### *config file* will be created after running `musicrepair -config`, and located at `$HOME/.musicrepair/config.json`

### Set them using ```-config```
```sh 
$ musicrepair -config                                               
Enter Spotify client ID : <enter Spotify client ID>  
Enter Spotify client secret : <enter Spotify client secret>                                     
```

## Installing

### Via Binary

Download the latest binary from the [releases page](https://github.com/kalbhor/MusicRepair/releases).

Make sure to add the binary to your `$PATH`

### Via Go
```sh
$ go get -u -v github.com/kalbhor/musicrepair
$ which musicrepair
$ $GOPATH/bin/musicrepair
```

## Usage

Initially, you'll have to add the Spotify credentials. 
```sh
$ musicrepair -config
```

After that, always a simple command
```sh
$ musicrepair
✨ 🍰
```


### Options
```
$ musicrepair -help

Usage of musicrepair:
  -config
    	If set, MusicRepair will ask for credentials
  -dir string
    	Specifies the directory where the music files are located (default "./")
  -recursive
    	If set, Musicrepair will run recursively in the given directory
  -revert
    	If set, Musicrepair will revert the files
  -threads int
    	Specify the number of threads to use (default 1)
```

## Discussions/Write-Ups
<p align="left">
    <a href="https://mavielinux.com/2016/12/11/musicrepair-pour-corriger-les-titresajouter-les-metadonnees-et-les-pochettes-de-vos-musiques/">
		<img width="70px" src="http://i.imgur.com/TklsaII.png"/>
	</a>
	&nbsp;&nbsp;&nbsp;
    <a href="http://blog.desdelinux.net/reparar-archivos-de-musica/">
		<img width="160px" src="http://i.imgur.com/eV1WxYZ.png"/>
	</a>
	&nbsp;&nbsp;&nbsp;
    <a href="https://www.reddit.com/r/learnpython/comments/5gzvcb/i_made_a_script_that_would_fix_your_music_files/">
		<img width="160px" src="http://i.imgur.com/Jk8PgIb.png"/>
	</a>
	&nbsp;&nbsp;&nbsp;
</p>


## Contribute

Found an issue? Post it in the [issue tracker](https://github.com/kalbhor/MusicRepair/issues).

Want to add another awesome feature? [Fork](https://github.com/kalbhor/MusicRepair/fork) this repository and add your feature, then send a pull request.

## License
The MIT License (MIT)
Copyright (c) 2017 Lakshay Kalbhor


================================================
FILE: main.go
================================================
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
)

var (
	root        = flag.String("dir", "./", "Specifies the directory where the music files are located")
	isRecursive = flag.Bool("recursive", false, "If set, Musicrepair will run recursively in the given directory")
	setConfig   = flag.Bool("config", false, "If set, MusicRepair will ask for credentials")
	isRevert    = flag.Bool("revert", false, "If set, Musicrepair will revert the files")
	threads     = flag.Int("threads", 1, "Specify the number of threads to use")
)

func main() {
	flag.Parse()

	if *setConfig {
		SetConfig()
		fmt.Println("Your config has been saved.")
		os.Exit(1)
	}

	config, err := LoadConfig()
	if err != nil {
		log.Fatal(err)
	}

	client, err := SpotifyAuth(config.ID, config.Secret)
	if err != nil {
		log.Fatal("Invalid spotify credentials. Error : %v", err)
	}

	fileList := WalkDir(*root) // List of all files to work on

	jobs := make(chan string)
	results := make(chan string)

	for w := 1; w <= *threads; w++ {
		if *isRevert {
			go RevertWorker(jobs, results)
		} else {
			go RepairWorker(client, jobs, results)
		}
	}

	for _, job := range fileList {
		jobs <- job
	}
	close(jobs)

	for r := 1; r <= len(fileList); r++ {
		fmt.Printf("[%v] %v\n", r, <-results)
	}

}


================================================
FILE: repair.go
================================================
package main

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/bogem/id3v2"
	"github.com/headzoo/surf/errors"
	"github.com/zmb3/spotify"
)


func RepairWorker(client spotify.Client, job <-chan string, results chan<- string) {
	for filePath := range job {
		_, fileName := filepath.Split(filePath)
		results <- fmt.Sprintf("Fixing : %v\n", fileName)
		if err := Repair(client, filePath); err != nil {
			results <- fmt.Sprintf("Error : %v\n", err)
		}
	}
}

func RevertWorker(job <-chan string, results chan<- string) {
	for filePath := range job {
		_, fileName := filepath.Split(filePath)
		results <- fmt.Sprintf("Reverting : %v\n", fileName)
		if err := Revert(filePath); err != nil {
			results <- fmt.Sprintf("Error : %v\n", err)
		}
	}
}


func Revert(path string) error {
	tag, err := id3v2.Open(path, id3v2.Options{Parse: true})
	if err != nil {
		return err
	}
	defer tag.Close()

	tag.DeleteAllFrames()
	if err = tag.Save(); err != nil {
		return err
	}

	return nil
}

func Repair(client spotify.Client, path string) error {
	tag, err := id3v2.Open(path, id3v2.Options{Parse: true})
	if err != nil {
		return err
	}

	if CheckFrames(tag.AllFrames()) {
		return errors.New("Already contains tags")
	}

	_, filename := filepath.Split(path)
	metadata, err := GetMetadata(client, filename[0:len(filename)-4])
	if err != nil {
		return err
	}

	tag.SetTitle(metadata.Title)
	tag.SetAlbum(metadata.Album)
	tag.SetArtist(strings.Join(metadata.Artists, ","))

	TrackNumber := strconv.Itoa(metadata.TrackNumber)
	tag.AddFrame("TRCK", id3v2.TextFrame{id3v2.EncodingUTF8, TrackNumber})

	DiscNumber := strconv.Itoa(metadata.DiscNumber)
	tag.AddFrame("TPOS", id3v2.TextFrame{id3v2.EncodingUTF8, DiscNumber})

	pic := id3v2.PictureFrame{
		Encoding:    id3v2.EncodingUTF8,
		MimeType:    "image/jpeg",
		PictureType: id3v2.PTFrontCover,
		Description: "Front cover",
		Picture:     metadata.Image,
	}

	tag.AddAttachedPicture(pic)
	if err = tag.Save(); err != nil {
		return err
	}

	return nil
}


================================================
FILE: spotify.go
================================================
package main

import (
	"context"
	"errors"
	"io/ioutil"
	"net/http"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/clientcredentials"

	"github.com/zmb3/spotify"
)

//Metadata : Structure for one track's metadata
type Metadata struct {
	Title       string
	Artists     []string
	Album       string
	Image       []byte
	DiscNumber  int
	TrackNumber int
}

//Load : Sets values from search results
func (m *Metadata) Load(track spotify.FullTrack) error {
	m.Title = track.SimpleTrack.Name
	m.Album = track.Album.Name
	m.DiscNumber = track.SimpleTrack.DiscNumber
	m.TrackNumber = track.SimpleTrack.TrackNumber

	for _, artist := range track.SimpleTrack.Artists {
		m.Artists = append(m.Artists, artist.Name)
	}

	imageURL := track.Album.Images[0].URL
	resp, err := http.Get(imageURL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	m.Image = b

	return nil
}

//GetMetadata : Searches spotify and returns a loaded metadata struct
func GetMetadata(client spotify.Client, query string) (*Metadata, error) {

	m := new(Metadata)

	results, err := client.Search(query, spotify.SearchTypeTrack)
	if err != nil {
		return nil, err
	} else if len(results.Tracks.Tracks) == 0 { // Search results were empty
		return nil, errors.New("Couldn't fetch metadata")
	}

	err = m.Load(results.Tracks.Tracks[0]) // Pass in the top result
	if err != nil {
		return m, err
	}
	return m, nil

}

//Auth : Returns a usable spotify "client" that can request spotify content
func SpotifyAuth(Id, Secret string) (spotify.Client, error) {
	config := &clientcredentials.Config{
		ClientID:     Id,
		ClientSecret: Secret,
		TokenURL:     spotify.TokenURL,
	}
	token, err := config.Token(context.Background())
	if err != nil {
		return spotify.Authenticator{}.NewClient(&oauth2.Token{}), err
	}

	client := spotify.Authenticator{}.NewClient(token)

	return client, nil
}


================================================
FILE: utils.go
================================================
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/bogem/id3v2"
)

type Config struct {
	ID     string
	Secret string
}

var configFolder string = path.Join(os.Getenv("HOME"), ".musicrepair")
var configPath string = path.Join(configFolder, "config.json")

func LoadConfig() (*Config, error) {
	file, err := os.Open(configPath)
	if err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(file)
	config := new(Config)
	err = decoder.Decode(&config)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func SetConfig() error {
	var id, secret string
	fmt.Print("Enter Spotify ID : ")
	fmt.Scanln(&id)
	fmt.Print("Enter Spotify Secret : ")
	fmt.Scanln(&secret)

	config := Config{id, secret}
	b, err := json.Marshal(config)
	if err != nil {
		return err
	}
	if err := os.Mkdir(configFolder, os.ModePerm); err != nil {
		return err
	}
	if err := ioutil.WriteFile(configPath, b, os.ModePerm); err != nil {
		return err
	}

	return nil
}

func WalkDir(root string) (fileList []string) {

	filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
		// Fills fileList with all mp3 files in the `root` file tree
		if err != nil {
			return err
		}
		if !*isRecursive && filepath.Dir(path) != filepath.Dir(root) {
			return filepath.SkipDir
		}
		if filepath.Ext(path) == ".mp3" {
			fileList = append(fileList, path)
		}
		return nil
	})
	return
}

// Checks if a file already contains metadata
func CheckFrames(frames map[string][]id3v2.Framer) bool {
	if _, ok := frames["TALB"]; !ok {
		return false
	}
	if _, ok := frames["TIT2"]; !ok {
		return false
	}
	if _, ok := frames["APIC"]; !ok {
		return false
	}
	if _, ok := frames["TRCK"]; !ok {
		return false
	}
	if _, ok := frames["TPOS"]; !ok {
		return false
	}
	return true
}
Download .txt
gitextract_xfdxu3u9/

├── .gitignore
├── LICENSE
├── README.md
├── main.go
├── repair.go
├── spotify.go
└── utils.go
Download .txt
SYMBOL INDEX (14 symbols across 4 files)

FILE: main.go
  function main (line 18) | func main() {

FILE: repair.go
  function RepairWorker (line 15) | func RepairWorker(client spotify.Client, job <-chan string, results chan...
  function RevertWorker (line 25) | func RevertWorker(job <-chan string, results chan<- string) {
  function Revert (line 36) | func Revert(path string) error {
  function Repair (line 51) | func Repair(client spotify.Client, path string) error {

FILE: spotify.go
  type Metadata (line 16) | type Metadata struct
    method Load (line 26) | func (m *Metadata) Load(track spotify.FullTrack) error {
  function GetMetadata (line 53) | func GetMetadata(client spotify.Client, query string) (*Metadata, error) {
  function SpotifyAuth (line 73) | func SpotifyAuth(Id, Secret string) (spotify.Client, error) {

FILE: utils.go
  type Config (line 14) | type Config struct
  function LoadConfig (line 22) | func LoadConfig() (*Config, error) {
  function SetConfig (line 36) | func SetConfig() error {
  function WalkDir (line 58) | func WalkDir(root string) (fileList []string) {
  function CheckFrames (line 77) | func CheckFrames(frames map[string][]id3v2.Framer) bool {
Condensed preview — 7 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
  {
    "path": ".gitignore",
    "chars": 432,
    "preview": "#### joe made this: http://goel.io/joe\n\n#### go ####\n# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2016 Lakshay Kalbhor\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 3143,
    "preview": "<p align=\"center\">\n  <b>Adds Metadata to Music files</b>\n</p>\n\n<p align=\"center\">\n    <a href=\"https://github.com/kalbho"
  },
  {
    "path": "main.go",
    "chars": 1268,
    "preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n)\n\nvar (\n\troot        = flag.String(\"dir\", \"./\", \"Specifies the direc"
  },
  {
    "path": "repair.go",
    "chars": 2015,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/bogem/id3v2\"\n\t\"github.com/headzoo/sur"
  },
  {
    "path": "spotify.go",
    "chars": 1923,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/clien"
  },
  {
    "path": "utils.go",
    "chars": 1808,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\n\t\"github.com/bogem/id3v2\"\n)\n"
  }
]

About this extraction

This page contains the full source code of the kalbhor/MusicRepair GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 7 files (11.4 KB), approximately 3.3k tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!