initial
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
test
|
||||||
|
data
|
||||||
|
*.txt
|
||||||
|
*.mp3
|
||||||
|
|
||||||
|
stream
|
||||||
97
main.go
Normal file
97
main.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
// "stream_enregistre"
|
||||||
|
// "stream_titre"
|
||||||
|
)
|
||||||
|
|
||||||
|
const autocutDelai = 60 //s
|
||||||
|
|
||||||
|
// Le filename reçu se verra juste ajouter une extension ici (.mp3 ou .index)
|
||||||
|
func Enregistre(filename string) {
|
||||||
|
startTime := time.Now()
|
||||||
|
fmt.Println("Started... ", startTime)
|
||||||
|
fmt.Println(" Attendre quelques minutes et regarder dans le repertoire courant... ☺ ")
|
||||||
|
fmt.Println(" <ctrl>C pour arrêter")
|
||||||
|
|
||||||
|
doneChanRecord := make(chan bool)
|
||||||
|
errChanRecord := make(chan error)
|
||||||
|
|
||||||
|
doneChanTitle := make(chan bool)
|
||||||
|
errChanTitle := make(chan error)
|
||||||
|
|
||||||
|
filenameStream := filename + ".mp3"
|
||||||
|
filenameIndex := filename + ".index"
|
||||||
|
|
||||||
|
go Enregistre_stream(doneChanRecord, errChanRecord, filenameStream)
|
||||||
|
go Enregistre_titres(doneChanTitle, errChanTitle, filenameIndex)
|
||||||
|
|
||||||
|
// Exécuter la fonction toutes les 'timer' millisecondes
|
||||||
|
ticker := time.Tick(autocutDelai * time.Second)
|
||||||
|
for range ticker {
|
||||||
|
Cut(filenameStream, filenameIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attendre que les goroutines aient terminé
|
||||||
|
successRecord := <-doneChanRecord
|
||||||
|
if !successRecord {
|
||||||
|
err := <-errChanRecord
|
||||||
|
fmt.Println("Erreur enregistrement :", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
successTitle := <-doneChanTitle
|
||||||
|
if !successTitle {
|
||||||
|
err := <-errChanTitle
|
||||||
|
fmt.Println("Erreur titres :", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Programme terminé")
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecoupeAll() {
|
||||||
|
|
||||||
|
Cut("data/stream_recorded_2023-03-03T09:10:15.stripped.mp3", "data/index2023-03-03T09:10:14.txt")
|
||||||
|
Cut("data/stream_recorded_2023-03-02T10:12:03.stripped.mp3", "data/index2023-03-02T10:12:03.txt")
|
||||||
|
|
||||||
|
//Cut("data/stream_recorded_2023-02-27T22:55:39.stripped.mp3", "data/index2023-02-27T22:55:39.txt")
|
||||||
|
|
||||||
|
Cut("data/stream_recorded_2023-03-01T20:20:48.stripped.mp3", "data/index2023-03-01T20:20:48.txt")
|
||||||
|
|
||||||
|
Cut("data/stream_recorded_2023-02-28T18:33:07.stripped.mp3", "data/index2023-02-28T18:33:07.txt")
|
||||||
|
|
||||||
|
Cut("data/stream_recorded_2023-03-01T08:16:54.stripped.mp3", "data/index2023-03-01T08:16:53.txt")
|
||||||
|
|
||||||
|
Cut("data/stream_recorded_2023-03-04T11:59:43.stripped.mp3", "data/index2023-03-04T11:59:43.txt")
|
||||||
|
Cut("data/stream_recorded_2023-03-04T13:39:29.stripped.mp3", "data/index2023-03-04T13:39:28.txt")
|
||||||
|
|
||||||
|
Cut("data/rolanradio_2023-03-04T14:58:40.mp3", "data/index2023-03-04T14:58:40.txt")
|
||||||
|
Cut("data/rolanradio_2023-03-04T18:26:17.mp3", "data/index2023-03-04T18:26:16.txt")
|
||||||
|
Cut("data/rolanradio_2023-03-04T22:17:41.mp3", "data/index2023-03-04T22:17:40.txt")
|
||||||
|
//Cut("data/", "data/")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
if len(os.Args) != 1 && len(os.Args) != 4 {
|
||||||
|
fmt.Println("Usage: ")
|
||||||
|
fmt.Println(" stream : enregistre et decoupe 'en direct' ")
|
||||||
|
fmt.Println(" stream cut ficher_mp3 fichier_index : découpe un mp3 compte tenu de l'index")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(os.Args) == 4 && os.Args[1] == "cut" {
|
||||||
|
mp3 := os.Args[2]
|
||||||
|
index := os.Args[3]
|
||||||
|
Cut(mp3, index)
|
||||||
|
} else {
|
||||||
|
strdate := time.Now().Format("2006-01-02T15:04:05")
|
||||||
|
filename := fmt.Sprintf("%s_%s", strdate, "enregistrement")
|
||||||
|
Enregistre(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
22
readme.md
Normal file
22
readme.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Enregistrement de rolandradio.net et séparation des morceaux en fichiers distincts
|
||||||
|
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
`go test` pour les tests unitaires
|
||||||
|
|
||||||
|
`go build` pour compiler
|
||||||
|
|
||||||
|
`CGO_ENABLED=0 ; go build` pour compiler pour des systemes avec une version de libc < 2.32
|
||||||
|
|
||||||
|
|
||||||
|
## Utilisation
|
||||||
|
|
||||||
|
Copier l'executable `stream` compilé ci-dessus dans un répertoire vide au préalable, et se placer dans ce répertoire. Ce répertoire contiendra tous les fichier mp3 téléchargé, plus le gros fichier mp3 complet téléchargé ainsi qu'un fichier d'index (timestamp des morceaux avec les titres)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
./stream
|
||||||
|
```
|
||||||
|
|
||||||
|
Attendre quelques minutes... Des fichiers mp3 vont apparaitre dans le répertoire courant
|
||||||
23
scripts/readme.md
Normal file
23
scripts/readme.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# petits scripts shell utilitaires
|
||||||
|
|
||||||
|
## xt pour strippe un "extraire" un bloc d'octets d'un binaire
|
||||||
|
|
||||||
|
Permet d'enlever le générique roland radio et les entetes mp3 ajouté a chaque stream au début
|
||||||
|
|
||||||
|
- premier parametre : la position du premier octet a récupérer
|
||||||
|
- deuxième parametre : le nombre d'octets a récupérer ( - pour aller jusqu'a la fin)
|
||||||
|
|
||||||
|
Dans l'exemple ci-dessous on utilise l'astuce `$((16#valeur_hexa))` pour convertir une valeur hexa en decimal quand la commande attend un decimal
|
||||||
|
|
||||||
|
```
|
||||||
|
./xt $((16#702a0)) 200000000 stream_recorded.mp3 > stream_recorded.stripped.mp3
|
||||||
|
|
||||||
|
./xt $((16#702a0)) - stream_recorded.mp3 > stream_recorded.stripped.mp3
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## astuce bash : conversion d'une chaine de date en nanosecondes
|
||||||
|
```
|
||||||
|
date -d "2023-02-27T16:30:06.386" +%s%N
|
||||||
|
```
|
||||||
42
scripts/xt
Executable file
42
scripts/xt
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
## extrait d'un fichier binaire 'file' a partir du 'first' octet (compris, commence a compter a 1)
|
||||||
|
## en comptant 'count' octets
|
||||||
|
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 first count file (chemin du fichier a traiter)"
|
||||||
|
echo " ou: $0 first count (avec l'utilisation de l'entrée standard)"
|
||||||
|
|
||||||
|
echo " Extrait les octets d'un fichier binaire 'file' ( ou de l'entrée standard) "
|
||||||
|
echo " a partir du 'first' octet (compris, commence a compter a 1)"
|
||||||
|
echo " en comptant 'count' octets"
|
||||||
|
echo " example : ./xt 4097 $((16#b0)) stream1.mp3 > stream.entete2.bin"
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Vérifie que 2 ou 3 arguments ont été passés
|
||||||
|
if [ "$#" -ne 2 ] && [ "$#" -ne 3 ]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Par défaut, utilise l'entrée standard comme entrée
|
||||||
|
input="/dev/stdin"
|
||||||
|
# Récupère les arguments passés en paramètre
|
||||||
|
first="$1"
|
||||||
|
count="$2"
|
||||||
|
if [ "$#" -eq 3 ]; then
|
||||||
|
input="$3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if [ "$count" = "-" ] ; then
|
||||||
|
# Lance tail ans head car on veut jusq'ua bout du fichier
|
||||||
|
cat "$input"| tail -c+$first
|
||||||
|
else
|
||||||
|
# Lance tail et head pour récupérer les octest demandés
|
||||||
|
cat "$input"| tail -c+$first |head -c$count
|
||||||
|
fi
|
||||||
144
stream_cut.go
Normal file
144
stream_cut.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const delaiDebut float64 = 2.00 //secondes
|
||||||
|
const delaiFin float64 = 0.20 //secondes
|
||||||
|
|
||||||
|
/* transforme la chaine de temps en ms
|
||||||
|
* format d'entrée : une chaine au format hh:mm:ss.ms (pas d'obligation d'avoir des heurs ou minutes )
|
||||||
|
*/
|
||||||
|
|
||||||
|
func strTimeToS(strTime string) (float64, error) {
|
||||||
|
tabTime := strings.Split(strTime, ":")
|
||||||
|
var (
|
||||||
|
timeMultiplicateur int = 1
|
||||||
|
time float64
|
||||||
|
)
|
||||||
|
for i := len(tabTime) - 1; i >= 0; i-- {
|
||||||
|
timeVal, err := strconv.ParseFloat(tabTime[i], 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.New("format invalide, nombre incorrect")
|
||||||
|
}
|
||||||
|
time += float64(timeMultiplicateur) * timeVal
|
||||||
|
timeMultiplicateur *= 60
|
||||||
|
}
|
||||||
|
|
||||||
|
return time, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInfo(ligne string) (float64, string, string, error) {
|
||||||
|
//split la ligne en 2 sur le premier espace
|
||||||
|
tabInfo := strings.SplitN(ligne, " ", 2)
|
||||||
|
strTime, strTitre := tabInfo[0], tabInfo[1]
|
||||||
|
|
||||||
|
debTime, err := strTimeToS(strTime)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
//split tmpTitre en artiste + titre en séparant par " - "
|
||||||
|
split := strings.SplitN(strTitre, " - ", 2)
|
||||||
|
var artiste, titre string
|
||||||
|
if len(split) > 1 {
|
||||||
|
artiste, titre = split[0], split[1]
|
||||||
|
} else {
|
||||||
|
titre = split[0]
|
||||||
|
}
|
||||||
|
return debTime, artiste, titre, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// on suppose un flux a 128kb/s soit 128000/8 kB/s
|
||||||
|
func timerToBytes(timer float64) int64 {
|
||||||
|
return int64(128000. / 8. * timer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cutAndSave(mp3File string, timerDebut float64, timerFin float64, artiste string, titre string) {
|
||||||
|
octetDebut := timerToBytes(timerDebut)
|
||||||
|
octetsFin := timerToBytes(timerFin)
|
||||||
|
|
||||||
|
//fmt.Printf("je coupe le fichier %s de %v a %v le mp3 de %s s'apellant %s \n", mp3File, timerDebut, timerFin, artiste, titre)
|
||||||
|
|
||||||
|
// ouvrir le fichier
|
||||||
|
f, err := os.Open(mp3File)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Erreur d'ouverture du fichier mp3 : %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// créer un tampon pour stocker les octets lus
|
||||||
|
tampon := make([]byte, octetsFin-octetDebut)
|
||||||
|
|
||||||
|
// lire les octets spécifiés à partir de la position spécifiée
|
||||||
|
n, err := f.ReadAt(tampon, int64(octetDebut))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Erreur de lecture du fichier mp3 de %d a %d soit %d octets: %v", octetDebut, octetsFin, n, err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Écriture du tableau d'octets dans un fichier
|
||||||
|
artiste = strings.ReplaceAll(artiste, "/", "_")
|
||||||
|
if artiste == "" {
|
||||||
|
artiste = "Inconnu"
|
||||||
|
}
|
||||||
|
titre = strings.ReplaceAll(titre, "/", "_")
|
||||||
|
|
||||||
|
var outputFile string = fmt.Sprintf("%s.__.%s.mp3", artiste, titre)
|
||||||
|
err = os.WriteFile(outputFile, tampon, 0644)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Erreur d'écriture du fichier mp3 du morceau: %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Cut(mp3File string, indexFile string) {
|
||||||
|
// Ouvrir le fichier index en lecture
|
||||||
|
fichier, err := os.Open(indexFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Erreur d'ouverture du fichier index : %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer fichier.Close()
|
||||||
|
|
||||||
|
// Créer un scanner pour lire le fichier ligne par ligne
|
||||||
|
scanner := bufio.NewScanner(fichier)
|
||||||
|
|
||||||
|
// Parcourir chaque ligne du fichier
|
||||||
|
timerPrecedent := -1.
|
||||||
|
artistePrecedent := ""
|
||||||
|
titrePrecedent := ""
|
||||||
|
for scanner.Scan() {
|
||||||
|
// Faire quelque chose avec la ligne, par exemple :
|
||||||
|
ligne := scanner.Text()
|
||||||
|
timer, artiste, titre, err := GetInfo(ligne)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Erreur de récupération des infos de la ligne %s : %v", ligne, err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if timerPrecedent != -1. {
|
||||||
|
cutAndSave(mp3File, timerPrecedent-delaiDebut, timer+delaiFin, artistePrecedent, titrePrecedent)
|
||||||
|
timerPrecedent = timer
|
||||||
|
} else {
|
||||||
|
timerPrecedent = 0. + delaiDebut
|
||||||
|
}
|
||||||
|
artistePrecedent = artiste
|
||||||
|
titrePrecedent = titre
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier s'il y a eu une erreur lors de la lecture du fichier
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Erreur de lecture du fichier index : %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
100
stream_cut_test.go
Normal file
100
stream_cut_test.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStrTimeToS_erreur(t *testing.T) {
|
||||||
|
testName := "Test erreur"
|
||||||
|
inputs := []string{"", "az", "0x00", "00:0x0:00.0", "0::0.0"}
|
||||||
|
expected := 0.0
|
||||||
|
for i, input := range inputs {
|
||||||
|
result, err := strTimeToS(input)
|
||||||
|
if err == nil {
|
||||||
|
msg := fmt.Sprintf("%s , n° %d | la valeur '%s' aurait du retourner une erreurmais ce n'est pas le cas. ", testName, i, input)
|
||||||
|
t.Errorf(msg, expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrTimeToS_0(t *testing.T) {
|
||||||
|
testName := "Test 0"
|
||||||
|
inputs := []string{"00", "0", "0.0", "0:0.0", "0:0", "0:0:0", "00:00:00", "0:00:0.0", "00:0:0", "00:00:00.0"}
|
||||||
|
expected := 0.0
|
||||||
|
for i, input := range inputs {
|
||||||
|
result, err := strTimeToS(input)
|
||||||
|
if err != nil || result != expected {
|
||||||
|
msg := fmt.Sprintf("%s , n° %d | la valeur '%s' aurait du retourner '%%f' mais on a obtenu '%%f' avec erreur : '%v'", testName, i, input, err)
|
||||||
|
t.Errorf(msg, expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrTimeToS_10(t *testing.T) {
|
||||||
|
testName := "Test 10"
|
||||||
|
inputs := []string{"10", "10.0", "0:10.0", "0:10", "0:0:10", "00:00:10", "0:00:10.0", "00:0:10"}
|
||||||
|
expected := 10.0
|
||||||
|
for i, input := range inputs {
|
||||||
|
result, err := strTimeToS(input)
|
||||||
|
if err != nil || result != expected {
|
||||||
|
msg := fmt.Sprintf("%s , n° %d | la valeur '%s' aurait du retourner '%%f' mais on a obtenu '%%f' avec erreur : '%v'", testName, i, input, err)
|
||||||
|
t.Errorf(msg, expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrTimeToS_100_42(t *testing.T) {
|
||||||
|
testName := "Test 100.42"
|
||||||
|
inputs := []string{"100.42", "1:40.420", "0:1:40.42", "0:1:40.42", "00:01:40.42"}
|
||||||
|
expected := 100.42
|
||||||
|
for i, input := range inputs {
|
||||||
|
result, err := strTimeToS(input)
|
||||||
|
if err != nil || result != expected {
|
||||||
|
msg := fmt.Sprintf("%s , n° %d | la valeur '%s' aurait du retourner '%%f' mais on a obtenu '%%f' avec erreur : '%v'", testName, i, input, err)
|
||||||
|
t.Errorf(msg, expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrTimeToS_4000(t *testing.T) {
|
||||||
|
testName := "Test 4000"
|
||||||
|
inputs := []string{"0001:06:040", "1:6:40", "01:06:40.00", "1:5:100.0"}
|
||||||
|
expected := 4000.
|
||||||
|
for i, input := range inputs {
|
||||||
|
result, err := strTimeToS(input)
|
||||||
|
if err != nil || result != expected {
|
||||||
|
msg := fmt.Sprintf("%s , n° %d | la valeur '%s' aurait du retourner '%%f' mais on a obtenu '%%f' avec erreur : '%v'", testName, i, input, err)
|
||||||
|
t.Errorf(msg, expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
func TestGetInfo_erreur(t *testing.T) {
|
||||||
|
testName := "Test getInfo"
|
||||||
|
inputs := []string{"00:01:aa.500 artiste - titre (etc) ☺"}
|
||||||
|
for i, input := range inputs {
|
||||||
|
_, _, _, err := GetInfo(input)
|
||||||
|
if err == nil {
|
||||||
|
msg := fmt.Sprintf("%s , n° %d | la valeur '%s' aurait du retourner une erreur.", testName, i, input)
|
||||||
|
t.Errorf(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetInfo(t *testing.T) {
|
||||||
|
testName := "Test getInfo"
|
||||||
|
inputs := []string{"00:00:00.500 artiste - titre (etc) ☺", "0:01:2.500 - titr - e (etc) ☺", "1:8:10.500 artiste titre (etc) ☺"}
|
||||||
|
expectedTime := []float64{0.5, 62.5, 4090.5}
|
||||||
|
expectedArtiste := []string{"artiste", "", ""}
|
||||||
|
expectedTitre := []string{"titre (etc) ☺", "titr - e (etc) ☺", "artiste titre (etc) ☺"}
|
||||||
|
for i, input := range inputs {
|
||||||
|
resultTime, resultArtiste, resultTitre, err := GetInfo(input)
|
||||||
|
if err != nil || resultTime != expectedTime[i] || resultArtiste != expectedArtiste[i] || resultTitre != expectedTitre[i] {
|
||||||
|
msg := fmt.Sprintf("%s , n° %d | la valeur '%s' aurait du retourner '%%f' '%%s' '%%s' mais on a obtenu '%%f' '%%s' '%%s' avec erreur : '%v'", testName, i, input, err)
|
||||||
|
t.Errorf(msg, resultTime, resultArtiste, resultTitre, expectedTime[i], expectedArtiste[i], expectedTitre[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
stream_enregistre.go
Normal file
77
stream_enregistre.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const stream_url = "http://streaming.rolandradio.net/rolandradio"
|
||||||
|
const stream_file = "./rolanradio_%s.mp3"
|
||||||
|
const sizeGeneriqueToStrip = 0x702A0 //octets
|
||||||
|
const bufferSize = 2048 //octets
|
||||||
|
|
||||||
|
func stopRecording() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enregistre le stream_url dans stream_file en ajoutant la date (%s)
|
||||||
|
// prend un channel de type booleen en entrée : true a la fin de la fonction OK, false si erreur
|
||||||
|
func Enregistre_stream(done chan bool, errChan chan error, filename string) {
|
||||||
|
// Obtenir la réponse HTTP depuis l'URL
|
||||||
|
resp, err := http.Get(stream_url)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error fetching stream:", err)
|
||||||
|
done <- false
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Créer un fichier local pour écrire les données du flux audio
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error creating file : %s", err)
|
||||||
|
done <- false
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
//fmt.Println("Rejet des ", sizeGeneriqueToStrip, " premiers octets du stream")
|
||||||
|
fmt.Println("Recording...")
|
||||||
|
|
||||||
|
// met a la poubelle les premiers octets du stream (entete + generique roland radio)
|
||||||
|
_, err = io.CopyN(io.Discard, resp.Body, sizeGeneriqueToStrip)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Erreur du strip de l'entête et générique du stream: %s", err)
|
||||||
|
done <- false
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := make([]byte, bufferSize)
|
||||||
|
|
||||||
|
var totalBytes int64
|
||||||
|
for {
|
||||||
|
n, err := resp.Body.Read(buffer)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error lecture buffer stream : %s", err)
|
||||||
|
done <- false
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.Write(buffer[:n])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error copying stream : %s", err)
|
||||||
|
done <- false
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalBytes += int64(n)
|
||||||
|
|
||||||
|
if stopRecording() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done <- true
|
||||||
|
}
|
||||||
12
stream_post_traitement.go
Normal file
12
stream_post_traitement.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
const delaiBeforeTrack = 350 //ms
|
||||||
|
const delaiAfterTrack = delaiBeforeTrack //ms
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enleve le début du fichier enregistré qui contient un entete mp3 et le "générique roland radio"
|
||||||
|
Une fois ce strip effectuié, le fichier obtenu est lisible par vlc et les delai des titres sont calés (a environ 300ms pret)
|
||||||
|
*/
|
||||||
|
func StripGenerique() {
|
||||||
|
println(delaiBeforeTrack, delaiAfterTrack)
|
||||||
|
}
|
||||||
90
stream_titre.go
Normal file
90
stream_titre.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const metadata_url = "http://streaming.rolandradio.net/status-json.xsl"
|
||||||
|
const index_file = "./index%s.txt"
|
||||||
|
const timer = 200 //ms
|
||||||
|
const dateFormat = "2006-01-02T15:04:05.000"
|
||||||
|
|
||||||
|
func get_actual_titre() string {
|
||||||
|
// Récupérer l'URL renvoyant du JSON
|
||||||
|
response, err := http.Get(metadata_url)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Erreur lors de la récupération de l'URL:", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
// Parser le JSON
|
||||||
|
var data map[string]interface{}
|
||||||
|
err = json.NewDecoder(response.Body).Decode(&data)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Erreur lors du parsing du JSON:", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
//fmt.Printf("%v", data)
|
||||||
|
|
||||||
|
// Récupérer le titre du premier flux
|
||||||
|
streamTitle := data["icestats"].(map[string]interface{})["source"].([]interface{})[0].(map[string]interface{})["title"].(string)
|
||||||
|
return streamTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDelai(startTime time.Time) string {
|
||||||
|
|
||||||
|
elapsedTime := time.Since(startTime)
|
||||||
|
|
||||||
|
hours := elapsedTime / time.Hour
|
||||||
|
elapsedTime -= hours * time.Hour
|
||||||
|
|
||||||
|
minutes := elapsedTime / time.Minute
|
||||||
|
elapsedTime -= minutes * time.Minute
|
||||||
|
|
||||||
|
seconds := elapsedTime / time.Second
|
||||||
|
elapsedTime -= seconds * time.Second
|
||||||
|
|
||||||
|
ms := elapsedTime / time.Millisecond
|
||||||
|
|
||||||
|
retour := fmt.Sprintf("%02d:%02d:%02d.%d", hours, minutes, seconds, ms)
|
||||||
|
return retour
|
||||||
|
}
|
||||||
|
|
||||||
|
func Enregistre_titres(done chan bool, errChan chan error, outFilename string) {
|
||||||
|
// ouvre le fichier contenant l'index
|
||||||
|
file, err := os.Create(outFilename)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
done <- false
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
titreActuel := ""
|
||||||
|
// Exécuter la fonction toutes les 'timer' millisecondes
|
||||||
|
ticker := time.Tick(timer * time.Millisecond)
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
for range ticker {
|
||||||
|
titre := get_actual_titre()
|
||||||
|
if titre != titreActuel {
|
||||||
|
titreActuel = titre
|
||||||
|
delai := getDelai(startTime)
|
||||||
|
info := fmt.Sprintf("%s %s\n", delai, titreActuel)
|
||||||
|
//fmt.Printf (info)
|
||||||
|
|
||||||
|
_, err = file.WriteString(info)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
done <- false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done <- true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user