Compare commits
2 Commits
6719d2aced
...
e49b0079d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e49b0079d3 | ||
|
|
b3969a5274 |
@@ -36,13 +36,13 @@ MGMT // Oracular Spectacular // Time to Pretend
|
||||
MGMT // Oracular Spectacular // The Youth
|
||||
MGMT // Oracular Spectacular // Electric Feel
|
||||
MGMT // Oracular Spectacular // Kids
|
||||
Kid Cudi // Man On The Moon_ The End Of Day // Day _N_ N
|
||||
Kid Cudi // Man On The Moon: The End Of Day // Day 'N' Nite (nightmare)
|
||||
Dan Deacon // Gliss Riffer // Sheathed Wings
|
||||
Dan Deacon // Gliss Riffer // When I Was Done Dying
|
||||
Dan Deacon // Gliss Riffer // Learning to Relax
|
||||
Dan Deacon // Gliss Riffer // Take It to the Max
|
||||
of Montreal // The Sunlandic Twins [Polyvinyl Record Co.] // Wraith Pinned To The Mist And Other Games
|
||||
of Montreal // The Sunlandic Twins [Polyvinyl Record Co.] // Oslo In The Summertime
|
||||
of Montreal // The Sunlandic Twins // Wraith Pinned to the Mist and Other Games
|
||||
of Montreal // The Sunlandic Twins // Oslo in the Summertime
|
||||
Candy Claws // Ceres & Calypso in the Deep Time // Into the Deep Time (One Sun)
|
||||
Candy Claws // Ceres & Calypso in the Deep Time // White Seal (Shell & Spine)
|
||||
Candy Claws // Ceres & Calypso in the Deep Time // Fell in Love (At the Water)
|
||||
@@ -56,26 +56,26 @@ Candy Claws // Ceres & Calypso in the Deep Time // Illusion (Fern Lake)
|
||||
Candy Claws // Ceres & Calypso in the Deep Time // Night Ela (Mystic Thing)
|
||||
Candy Claws // Ceres & Calypso in the Deep Time // Where I Found You (One Star)
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Fight Test
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // One More Robot + Sympathy 3000-21
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // One More Robot / Sympathy 3000-21
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Yoshimi Battles the Pink Robots, Part 1
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Yoshimi Battles the Pink Robots, Part 2
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // In the Morning of the Magicians
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Ego Tripping at the Gates of Hell
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Are You a Hypnotist
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Are You a Hypnotist??
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // It’s Summertime
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Do You Realize
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Do You Realize??
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // All We Have Is Now
|
||||
The Flaming Lips // Yoshimi Battles the Pink Robots // Approaching Pavonis Mons by Balloon (Utopia Planitia)
|
||||
Gold Panda // Companion // Quitter's Raga
|
||||
Jonathan Wilson // Gentle Spirit // Rolling Universe
|
||||
Jhene Aiko // Trip // LSD
|
||||
Jhene Aiko // Trip // Jukai
|
||||
Jhene Aiko // Trip // While We_re Young
|
||||
Jhene Aiko // Trip // Sativa (Feat_ Swae Lee)
|
||||
Jhene Aiko // Trip // While We're Young
|
||||
Jhene Aiko // Trip // Sativa (Feat. Swae Lee)
|
||||
Jhene Aiko // Trip // Nobody
|
||||
Jhene Aiko // Trip // Overstimulated
|
||||
Jhene Aiko // Trip // Psilocybin (Love In Full Effect) (
|
||||
Jhene Aiko // Trip // Ascension (Feat_ Brandy)
|
||||
Jhene Aiko // Trip // Psilocybin (Love In Full Effect) (Feat. Dr. Chill)
|
||||
Jhene Aiko // Trip // Ascension (Feat. Brandy)
|
||||
Jhene Aiko // Trip // Trip
|
||||
Flying Lotus // Cosmogramma Alt Takes // Clock Catcher (harp arrangement)
|
||||
Flying Lotus // Cosmogramma Alt Takes // Melting3
|
||||
@@ -85,8 +85,8 @@ Flying Lotus // Cosmogramma Alt Takes // Computer Face
|
||||
Flying Lotus // Cosmogramma Alt Takes // Galaxy in Janaki (2008 version)
|
||||
Flying Lotus // Cosmogramma Alt Takes // Archway (Teebs remix)
|
||||
Sam Gellaitry // Escapism II // The Gateway
|
||||
Sam Gellaitry // Viewfinder Vol. 1 // PHOSPHENE - Viewfinder
|
||||
Hudson Lee // Soul Arcs // Soul Arcs
|
||||
Sam Gellaitry // Viewfinder Vol. 1: PHOSPHENE // Viewfinder
|
||||
Hudson Lee // Reflex Angle // Soul Arcs
|
||||
Black Moth Super Rainbow // Cobra Juicy // Windshield Smasher
|
||||
Black Moth Super Rainbow // Cobra Juicy // Like a Sundae
|
||||
Black Moth Super Rainbow // Cobra Juicy // Hairspray Heart
|
||||
@@ -98,4 +98,4 @@ Black Moth Super Rainbow // Cobra Juicy // I Think I’m Evil
|
||||
Black Moth Super Rainbow // Cobra Juicy // Dreamsicle Bomb
|
||||
Black Moth Super Rainbow // Cobra Juicy // Blurring My Day
|
||||
Black Moth Super Rainbow // Cobra Juicy // Spraypaint
|
||||
Boards Of Canada // Geogaddi // Music Is Math
|
||||
Boards of Canada // Geogaddi // Music Is Math
|
||||
@@ -1,3 +1,4 @@
|
||||
cd scripts
|
||||
deno --allow-read --allow-write clean.ts
|
||||
@REM deno --allow-read --allow-write clean.ts
|
||||
for %%f in (.\*.m3u8) do .\clean-id3.exe %%f
|
||||
cd ..
|
||||
|
||||
BIN
scripts/clean-id3.exe
Normal file
BIN
scripts/clean-id3.exe
Normal file
Binary file not shown.
8
scripts/clean-id3/go.mod
Normal file
8
scripts/clean-id3/go.mod
Normal file
@@ -0,0 +1,8 @@
|
||||
module zomo.dev/clean-id3
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/Eyevinn/hls-m3u8 v0.6.0 // indirect
|
||||
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 // indirect
|
||||
)
|
||||
4
scripts/clean-id3/go.sum
Normal file
4
scripts/clean-id3/go.sum
Normal file
@@ -0,0 +1,4 @@
|
||||
github.com/Eyevinn/hls-m3u8 v0.6.0 h1:i4eyofj5zStgUPcy+UwUQ4oOgcLJVGbrw4XOcxVMVw8=
|
||||
github.com/Eyevinn/hls-m3u8 v0.6.0/go.mod h1:9jzVfwCo1+TC6yz+TKDBt9gIshzI9fhVE7M5AhcOSnQ=
|
||||
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 h1:OtSeLS5y0Uy01jaKK4mA/WVIYtpzVm63vLVAPzJXigg=
|
||||
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8/go.mod h1:apkPC/CR3s48O2D7Y++n1XWEpgPNNCjXYga3PPbJe2E=
|
||||
183
scripts/clean-id3/main.go
Normal file
183
scripts/clean-id3/main.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/dhowden/tag"
|
||||
)
|
||||
|
||||
const ID3_TEMPLATE = "{{.Artist}} // {{.Album}} // {{.Title}}"
|
||||
|
||||
type Tags struct {
|
||||
Title string
|
||||
Album string
|
||||
Artist string
|
||||
AlbumArtist string
|
||||
Composer string
|
||||
Genre string
|
||||
Year int
|
||||
Track int
|
||||
TrackCount int
|
||||
Disc int
|
||||
DiscCount int
|
||||
}
|
||||
|
||||
func (t *Tags) Validate() error {
|
||||
if t.Title == "" {
|
||||
return errors.New("missing Title")
|
||||
}
|
||||
if t.Album == "" {
|
||||
return errors.New("missing Album")
|
||||
}
|
||||
if t.Artist == "" {
|
||||
return errors.New("missing Artist")
|
||||
}
|
||||
if t.AlbumArtist == "" {
|
||||
return errors.New("missing AlbumArtist")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := mainErr()
|
||||
if err != nil {
|
||||
log.Fatal("ERROR: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func mainErr() error {
|
||||
// file from arg
|
||||
playlistFile := os.Args[1]
|
||||
|
||||
newFileLines, err := loadPlaylistFile(playlistFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newFileContent := strings.Join(newFileLines, "\n")
|
||||
|
||||
return writeFile(playlistFile, newFileContent)
|
||||
}
|
||||
|
||||
// returns array of all id3 tags
|
||||
func loadPlaylistFile(playlistFile string) ([]string, error) {
|
||||
file, err := os.Open(playlistFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
playlistTags := make([]string, 0)
|
||||
lineNumber := 0
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
// line by line
|
||||
line := scanner.Text()
|
||||
tags, err := loadPlaylistFileLine(line)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s line %d: %s", playlistFile, lineNumber, err)
|
||||
}
|
||||
if tags != nil {
|
||||
playlistTags = append(playlistTags, *tags)
|
||||
}
|
||||
|
||||
lineNumber++
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return playlistTags, nil
|
||||
}
|
||||
|
||||
// ignore m3u8 tags
|
||||
func loadPlaylistFileLine(line string) (*string, error) {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if []rune(line)[1] == '#' {
|
||||
return nil, nil
|
||||
}
|
||||
if len(line) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tags, err := loadSongFile(line)
|
||||
return &tags, err
|
||||
}
|
||||
|
||||
// returns id3 tags
|
||||
func loadSongFile(fileName string) (string, error) {
|
||||
// load file
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
tags, err := tag.ReadFrom(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// parse tags
|
||||
track, trackCount := tags.Track()
|
||||
disc, discCount := tags.Disc()
|
||||
|
||||
loadedTags := Tags{
|
||||
Title: tags.Title(),
|
||||
Album: tags.Album(),
|
||||
Artist: tags.Artist(),
|
||||
AlbumArtist: tags.AlbumArtist(),
|
||||
Composer: tags.Composer(),
|
||||
Genre: tags.Genre(),
|
||||
Year: tags.Year(),
|
||||
Track: track,
|
||||
TrackCount: trackCount,
|
||||
Disc: disc,
|
||||
DiscCount: discCount,
|
||||
}
|
||||
|
||||
// Ivy Lab // Infinite Falling Ground // Touch
|
||||
if loadedTags.Artist == "Ivy Lab" {
|
||||
log.Println(loadedTags.Title)
|
||||
}
|
||||
|
||||
err = loadedTags.Validate()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return id3Template(loadedTags)
|
||||
}
|
||||
|
||||
// converts an id3 tag into a string
|
||||
func id3Template(tags Tags) (string, error) {
|
||||
tmpl := template.Must(template.New("id3template").Parse(ID3_TEMPLATE))
|
||||
|
||||
bufPath := bytes.NewBufferString("")
|
||||
err := tmpl.Execute(bufPath, tags)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return bufPath.String(), nil
|
||||
}
|
||||
|
||||
// outputs all strings as a file
|
||||
func writeFile(inFileName string, fileContent string) error {
|
||||
inFileExt := path.Ext(inFileName)
|
||||
extIndex := strings.LastIndex(inFileName, inFileExt)
|
||||
newFileName := inFileName[:extIndex] + ".txt"
|
||||
|
||||
return os.WriteFile(newFileName, []byte(fileContent), 0666)
|
||||
}
|
||||
9
scripts/clean-id3/reame.md
Normal file
9
scripts/clean-id3/reame.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# clean-id3
|
||||
|
||||
This will take in an m3u8 file, and will create a text file with just the song name info taken from the ID3 tags
|
||||
|
||||
## usage
|
||||
|
||||
`./clean ./01-01.m3u8`
|
||||
|
||||
This will create a file `./01-01.txt`
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* this is a deno script to clean up the m3u8 files from the music player
|
||||
*
|
||||
*/
|
||||
|
||||
import { opendir, readFile, writeFile } from 'node:fs/promises';
|
||||
import * as path from "jsr:@std/path";
|
||||
import { Dirent } from 'node:fs';
|
||||
|
||||
/**
|
||||
* longEnough will be true if splitting the line gave 3+ sections, and false if it's too short
|
||||
*
|
||||
* @param cleanLine
|
||||
* @returns [cleanLine, longEnough]
|
||||
*/
|
||||
function replaceLineDashes(cleanLine: string): [string, boolean] {
|
||||
const spl = cleanLine.split(' - ')
|
||||
if (spl.length < 3) {
|
||||
return [spl.join(' // '), false]
|
||||
}
|
||||
|
||||
const [spl1, spl2, ...splRemain] = spl
|
||||
const spl3 = splRemain.join(' - ')
|
||||
|
||||
return [[spl1, spl2, spl3].join(' // '), true]
|
||||
}
|
||||
|
||||
// loop through every line of the file and clean up each line
|
||||
function cleanFile(dirent: Dirent, content: string): string {
|
||||
const lines = content.split('\n')
|
||||
const cleanLines: string[] = []
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim()
|
||||
|
||||
if (!line || line.startsWith("#")) {
|
||||
continue
|
||||
}
|
||||
|
||||
const ext = path.extname(line)
|
||||
const name = path.basename(line, ext)
|
||||
|
||||
let cleanLine = name
|
||||
.replace(/ - Single/g, '')
|
||||
.replace(/- \d+ -/g, '-')
|
||||
|
||||
const [updatedCleanLine, longEnough] = replaceLineDashes(cleanLine)
|
||||
cleanLine = updatedCleanLine
|
||||
|
||||
if (!longEnough) {
|
||||
cleanLine = `BAD LINE --- ${cleanLine}`
|
||||
const fileName = path.basename(dirent.name)
|
||||
console.log(`Bad Line found in ${fileName} on line ${i+1}: \t${line}`)
|
||||
}
|
||||
|
||||
cleanLines.push(cleanLine)
|
||||
}
|
||||
|
||||
return cleanLines.join('\n') + '\n'
|
||||
}
|
||||
|
||||
// get details from the file, ensure it's a playlist file, then store the updated file contents to a txt file
|
||||
async function processFile(dirent: Dirent) {
|
||||
if (dirent.isFile()) {
|
||||
const ext = path.extname(dirent.name)
|
||||
const name = path.basename(dirent.name, ext)
|
||||
const dirname = path.dirname(dirent.name)
|
||||
const newPath = path.join(dirname, `${name}.txt`)
|
||||
|
||||
if (ext.toUpperCase() === ".M3U8") {
|
||||
const fileContent = await readFile(dirent.name)
|
||||
const cleanContent = cleanFile(dirent, fileContent.toString())
|
||||
await writeFile(newPath, cleanContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop through every file in the current directory
|
||||
try {
|
||||
const dir = await opendir('./');
|
||||
for await (const dirent of dir) {
|
||||
await processFile(dirent)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
Reference in New Issue
Block a user