Side 1 av 3 123 SisteSiste
Resultater 1 til 20 av 54
Abonnér på denne tråden
  1. #1
    Æresmedlem Asbjørn's Avatar
    Ble medlem
    Mar 2006
    Innlegg
    21,686
    Tagget i
    42 Innlegg

    Nyttige scripts for å gjøre ting med musikksamlingen

    Jeg tenkte at det kanskje kunne være interessant med en tråd hvor vi samler opp forskjellige scripts (batch-filer, python scripts, ...) som gjør nyttige småjobber på et digitalt musikkarkiv. Det er alltid lettere å starte med noe som funker og så modifisere til eget bruk enn å begynne på bar bakke.

    Som en lett oppvarming, en batchjobb som pakker ut alle HDCD-kodede flac-filer i en mappe til nye flac-filer i en undermappe. HDCD-formatet kan i prinsippet kode 20 bits dynamikk i en 16-bits fil, så vi lager 24-bits filer for å være sikker på at alt får plass. Visstnok skal dBpoweramp være i stand til å gjøre dette automatisk ved ripping, men av en eller annen uforklarlig grunn har aldri dette fungert hos meg, så det må gjøres etter ripping. Programmet som gjør selve jobben heter hdcd.exe og kan lastes ned herfra:
    HDCD Software Decoder - Doom9's Forum
    HDCD | Luke Skaff

    Det er et par småting å passe på. hdcd.exe opererer på wav-filer, så vi må først pakke ut flac til wav, pakke ut HDCD-kodingen til en 24-bits wav-fil, og så konvertere den til flac igjen. Dette har en tendens til å gå hardt utover metadata, så vi tar vare på metadata i en egen tekstfil og henger dem på igjen til slutt. Det kan vi gjøre med programmene flac.exe og metaflac.exe. De finnes her:
    FLAC - download

    Riktignok må ReplayGain-verdiene beregnes på nytt helt til slutt, men det er en overkommelig jobb, så det tar vi ikke med i batchjobben i denne omgang. Da blir det slik:

    @echo off
    REM Batch job to unpack 16-bit HDCD files to 24-bit regular WAV files
    REM Needs a final pass "by hand" to set correct replay gain and to add [HDCD] to album tag

    IF NOT EXIST HDCD MKDIR HDCD
    copy Folder.jpg HDCD\Folder.jpg

    FOR %%A IN (*.flac) DO (
    echo --------------
    echo %%A
    C:\Programfiler\FLAC\metaflac.exe --export-tags-to="HDCD\temp.txt" "%%A"
    C:\Programfiler\FLAC\flac.exe -d -f -o "HDCD\temp1.wav" "%%A"
    C:\Programfiler\HDCD\hdcd.exe -o "HDCD\temp2.wav" "HDCD\temp1.wav"
    del "HDCD\temp1.wav"
    C:\Programfiler\FLAC\flac.exe -5 -f -o "HDCD\%%~nA.flac" "HDCD\temp2.wav"
    del "HDCD\temp2.wav"
    C:\Programfiler\FLAC\metaflac.exe --import-tags-from="HDCD\temp.txt" --import-picture-from=Folder.jpg "HDCD\%%~nA.flac"
    del "HDCD\temp.txt"
    )


    Den kodebiten lagres til en fil, f eks deHDCD.bat, og kjøres ved å dobbeltklikke på det ikonet i en vanlig Windows-mappe. Dette er vel ikke helt best practice-koding, ettersom adressen til de forskjellige programmene er hardkodet heller enn hentet fra environment-variabler, men det funker for meg.
    Siste redigert av Asbjørn; 02.06.2013 kl. 19:08.

  2. #2
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    Dersom du ønsker å legge til [HDCD] til album-tagen så kan du såpne temp.txt fila di, finne album-tagen og appende " [HDCD]" og dermed legge til denne som en del av scriptet. Python er spesielt god på slikt.

  3. #3
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    Jeg har forøvrig et bash-script som jeg benytter for all inkommende FLAC. Dette er imidlertid dratt ganske mye lenger enn en liten "quick fix" siden det benyttes igjen og igjen og fungerer som en viktig integritetssjekk og arbeidsbesparende prosess hos meg. Det spørs om dette egentlig har noen nytteverdi for andre, spesielt fordi det også er rettet mot Linux som platform.

    Scriptet går gjennom en mappestruktur med FLAC-filer, reencoder dem enten alle som en, alternativt sjekker versjonen de er encodet med for så å kun reenkode de som ikke er siste versjon. Du kan som et siste valg bare kjøre en test for sjekke integriteten på filene.

    I tillegg kverner scriptet gjennom tags når filen uansett reenkodes med et tilhørende python script som automatiserer et par ting:
    - Fjerner alle tags som ikke står i listen over tags du ønsker å ha i biblioteket for å unngå tvetydige tags og feil informasjon i biblioteker, selv om dine primære tags er satt korrekt.
    - Korrigerer åpenbare feil som ekstra mellomrom på slutten/begynnelsen av en tag og tilsvarende
    - Sørger for korrekt skrivemåte i henhold til ønsket visuell profil på en rekke ord. F.eks stor I, store romertall, for, in, with osv med liten forbokstav, liten bokstav på the med mindre setningen starter med The osv. Det er lett å legge til ting man ønsker å fikse på som er såpass generisk at det ikke gir en masse uønskede variasjoner når språket f.eks blir norsk på låttitler.

    Scriptet benytter så mange prosesser du ønsker at det skal benytte så du kan f.eks kjøre fire parallelle reencodings-prosesser om du har en quad core. Dette øker hastigheten på eksekveringen drastisk.

    I tillegg har jeg et script som er livsfarlig som jeg anbefaler at dere holder dere unna, som går gjennom en mappestruktur og sørger for at kun de filene jeg vil at skal være der er der. Det vil si *.flac og folder.jpg, og INGENTING annet. I tillegg vil scriptet ta alle folder.jpg filer som er over en viss størrelse, dvs 1000x1000, og krympe disse med en høykvalitets lanczos-algoritme. Dette gjøres fordi dette gir hurtigere respons i avspillerløsningen når man populerer album-browsing vinduer i f.eks MPad og JRiver.

    Vedlagt er exporten fra trunk i SVN-repoet mitt. Det vil si siste gjeldende versjoner av scriptene. Jeg måtte zip-e tar.gz filen for å lov til å laste den opp her. Du må altså pakke ut både zip-filen og tar.gz for å komme til scriptene.
    Vedlagte filer Vedlagte filer
    Siste redigert av marsboer; 02.06.2013 kl. 19:41.

  4. #4
    Æresmedlem Asbjørn's Avatar
    Ble medlem
    Mar 2006
    Innlegg
    21,686
    Tagget i
    42 Innlegg
    De var ganske godt innpakket, ja, og så var det klassikeren med CR LF eller bare LF når ting flyttes mellom Linux og Windows. Men det gikk jo.

    Du har tydeligvis automatisert mye som jeg fortsatt gjør manuelt, sånn som riktig valg av stor/liten forbokstav, fjerning av uvedkommende tags, osv. Jeg gjør det som en manuell workflow for å ha litt bedre kontroll med hva som skjer. Jeg gjør alltid en konvertering med dBpoweramp til slutt, både for å sette ReplayGain-tags og for å være sikker på at det er en konsistent versjon av flac-codec'en. Scriptet clean_flac_dir.sh ser bortimot livsfarlig ut, ja. Der ville nok jeg hardkodet eksakt hvilke mapper den får lov til å snuse på i selve scriptet.

    På Windows er det ikke så veldig mye man kan få gjort i et shell script, og det lille man kan gjøre har en ubegripelig syntaks, så jeg har blitt fan av Python. Der bruker jeg versjon 2.7 fortsatt av hensyn til kompatibilitet med forskjellige biblioteker. For eksempel, hvis man funderer på om det er noen uutpakkede HDCD-filer på disken slik at det forrige scriptet er noe å ha, så kan man først lage et lite Python-script isFlacFileCDRez.py for å sjekke om filen er 16/44.1, og så kjøre hdcd.exe for å diagnostisere om det er HDCD eller ikke.

    isFlacFileCDRez.py ser slik ut:

    #!/usr/bin/env python
    # -*- coding=latin_1 -*-

    import os
    import re
    import string
    import codecs
    import sys
    from mutagen.flac import FLAC

    # takes one command line argument, assumed to be the name of a FLAC file,
    # and checks if it is CD resolution or not. Returns 0 if CD, 1 if not.

    audio = FLAC(sys.argv[1])
    bits = audio.info.bits_per_sample
    rate = audio.info.sample_rate

    if (bits == 16 and rate == 44100):
    sys.exit(0)
    else:
    sys.exit(1)



    Det scriptet kan kalles fra en batch-fil på denne måten:

    @echo off
    REM Batch job to identify 16-bit HDCD files

    FOR /R %%A IN (*.flac) DO (
    c:\Python27\python.exe isFlacFileCDRez.py "%%A"
    IF NOT ERRORLEVEL 1 (
    C:\Programfiler\FLAC\flac.exe -d -f -s -o C:\TEMP\temp.wav "%%A"
    C:\Programfiler\HDCD\hdcd.exe -i -s C:\TEMP\temp.wav
    IF NOT ERRORLEVEL 1 echo %%A
    )
    )

    del C:\TEMP\temp.wav
    pause "Press a key to continue..."



    Scriptet sjekker systematisk om hver 16/44.1 flac-fil er HDCD eller ikke. Hvis den er det, skriver den ut navnet på filen. Kommandoen FOR /R gjør at den kverner gjennom gjeldende mappe og alle undermapper. Lokasjonen for Python-interpreteren python.exe og programmene hdcd.exe og flac.exe er fortsatt hardkodet i scriptet. Vi kan lage en liten batch-fil med den teksten i, kalle den ScanHDCD.bat, og starte den fra et cmd-vindu som

    cd /d Z:\media\Music
    ScanHDCD > scanhdcd.txt


    Da kverner den gjennom hele musikkbiblioteket og lager en fil, scanhdcd.txt, som inneholder navnet på alle flac-filer som inneholder HDCD-koding. Den filen blir slik:


    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\07 - Joni Mitchell - Rainy Night House.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\12 - Joni Mitchell - The Circle Game.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\04 - Joni Mitchell - Ladies of the Canyon.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\02 - Joni Mitchell - For Free.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\05 - Joni Mitchell - Willy.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\01 - Joni Mitchell - Morning Morgantown.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\11 - Joni Mitchell - Woodstock.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\06 - Joni Mitchell - The Arrangement.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\03 - Joni Mitchell - Conversation.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\09 - Joni Mitchell - Blue Boy.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\08 - Joni Mitchell - The Priest.flac
    Z:\media\Music\Joni Mitchell\Ladies of the Canyon\10 - Joni Mitchell - Big Yellow Taxi.flac
    .
    .
    .



    Det var noen overraskelser nedover i listen, selv om langtfra alle "HDCD-filene" brukte de funksjonene til noe nyttig.
    Siste redigert av Asbjørn; 02.06.2013 kl. 21:05.

  5. #5
    Æresmedlem Pink_Panther's Avatar
    Ble medlem
    Mar 2006
    Innlegg
    13,684
    Tagget i
    2 Innlegg
    Akkurat.....
    Er det fremskritt om en kannibal bruker kniv og gaffel?

    Stanislaw Jerzy Lec


  6. #6
    Æresmedlem Asbjørn's Avatar
    Ble medlem
    Mar 2006
    Innlegg
    21,686
    Tagget i
    42 Innlegg
    Hvilken del av FOR /R %%A IN (*.flac) DO er det du ikke forstår?

  7. #7
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    På windows har man heldigvis nå fått powershell som et mer fullverdig alternativ til CMD. Jeg finner personlig at f.eks php, python, bash og cmd er relativt logiske språk, men når det gjelder powershell er det fremdeles RTFM som gjelder på annet enn de enkleste script for min del. Jeg har store problemer med å få syntaksen og funksjonene i powershell inn i fingrene. Jeg har laget mange powershell til administrative oppgaver i forbindelse med drift av Windows-systemer, men jeg har ingen Window-maskiner involvert i vedlikehold av mediabiblioteket.

  8. #8
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    Jeg har også et annet script som utfører en nyttig jobb her hjemme. Det er et script som kjøres automatisk på serveren og som konverterer FLAC-biblioteket til mp3 og som samtidig bevarer tags og kalkulerer ny gyldig replaygain med både track og album gain tilpasset mp3. Denne er kjekk å ha dersom man enkelt vil ha mp3-varianter av filene tilgjengelig for mobile enheter og tilsvarende.

    Dette finnes selvsagt i ulike former fra før, men jeg utviklet mitt script fordi jeg synes tag-støtten ofte var svært mangelfull samt at scriptet mitt er laget noenlunde intelligent slik at kun endrede eller nye flac filer vil transkodes til mp3 ved neste gjennomkjøring, og scriptet vil også detektere om det er kun tagen som må oppdateres dersom FLAC-lyddataene er uendret. Poenget var å kunne kjøre dette direkte på mediaserveren, siden dette går mange ganger raskere enn å gjøre det samme over gigabitnettverket, spesielt siden scriptet kjører transkodingen i parallell på flere CPU-kjerner om ønskelig.

    Sox er også involvert i kjeden, så om ønskelig kan man prosessere alle filene på ulikt vis mens de konverteres til mp3.

    Det enkleste er å sjekke ut wiki-siden med informasjon om scriptet:

    https://wiki.proikt.com/index.php/FL...P3_mirror_sync

    Per nå er v2.3.2 siste versjon. Jeg ha kjørt denne versjoner i mange måneder uten at nye bugs har dukket opp (det var en del småbugs i begynnelsen, spesielt med tanke på håndtering av filnavn og tags med sære spesialtegn, f.eks islandsk og kyrillisk). Scriptet er tilgjengelig på wikien, men for å dra opp nerdefaktoren litt i denne tråden, som jo må sies å være litt laber ( ), kan jeg jo lime inn koden her også:

    Kode:
    #!/bin/bash
    #
    # Author: Hans-Kristian Bakke
    #
    ####
    
    # Important: both paths must end with a trailing slash!
    export musicdir="/srv/common/Media/Audio/FLAC/"
    export mp3_destdir="/srv/common/Temp/MP3/"
    
    # Set VBR quality for the mp3 files
    export vbr_quality=0
    
    # Configure tag options for eyeD3
    export tag_options="-2 --to-v2.3 --encoding=utf16 --no-config"
    
    # Number of concurrent processes to run. In addition the decoding and encoding already runs in parallel, but the lame encoding is much more demanding than the sox decoding.
    # This value should normally be set to the count of your actual physical CPU cores.
    concurrency=4
    
    # Set to "yes" to show full output. This disables concurrency to avoid a visual mess.
    export verbose="no"
    
    ####
    
    # Check if the script is already running
    for pid in $(pgrep "$(basename "$BASH_SOURCE")"); do
        if [[ $pid -ne $$ ]]; then
            echo "Script is already running. Exiting..."
            exit 0
        fi
    done
    
    # Make sure we have all the dependencies
    for cmd in lame eyeD3 sox rsync metaflac; do
        command -v $cmd >/dev/null 2>&1 || { echo "'$cmd' is required, but not available. Exiting..." 1>&2; exit 1; }
    done
    
    # Text styling presets for prettier output
    export red=$(tput bold && tput setaf 1)
    export green=$(tput bold && tput setaf 2)
    export yellow=$(tput bold && tput setaf 3)
    export turquoise=$(tput bold && tput setaf 6)
    export text_reset=$(tput sgr0)
    
    # Make sure concurrency is set to a sane value
    if [[ $concurrency -lt 1 || $verbose == "yes" ]]; then
        concurrency=1
    fi
    
    # Function that removes normal text output from script if verbose is not active
    verbosity () {
        exec 3>&1 4>&2
        if ! [[ $verbose == "yes" ]]; then
            exec &>/dev/null
        fi
    }
    
    # Function for printing a message in non verbose mode
    print_nv () {
        exec 1>&3
        echo "$@"
        verbosity
    }
    
    # Function for handling user aborts
    abort () {
        print_nv "[ ${red}ABORT${text_reset} ]  Aborted by user!"
        rm -f $need_replaygain
        exit 1
    }
    
    # Function for syncing the library structure from source to destination directory
    sync_library_structure () {
        if ! rsync -aviHH --stats --delete --filter='-p *.mp3' --exclude='*.[Ff][Ll][Aa][Cc]' "$1" "$2"; then
            echo "[ ${red}ERROR${text_reset} ]  Library structure sync failed!"
            exit 1
        else
            echo "[ ${green}STRUCTURE SYNC OK${text_reset} ]  Library structure sync from '$musicdir' to '$mp3_destdir' completed successfully"
        fi
        
        verbosity
    }
    
    # Function that encodes a mp3 file from a flac file, but only if the mp3 version does not exist or if the modify time is not the same
    flac_to_mp3 () {
        local flac="$1"
        local mp3=$(echo "${flac/#"${musicdir}"/${mp3_destdir}}" | sed -r 's/\..{4}$/.mp3/')
        local timestamp_flac=$(stat -c "%Y" "$flac")
        local retag_only=false
        local tags_to_add=()
        local tag_options=($tag_options)
        
        # Create vorbis comment to id3v2 tag mapping
        declare -A tags
        tags["ALBUMARTIST"]="TPE2"
        tags["ALBUM ARTIST"]="TPE2"
        tags["ARTIST"]="TPE1"
        tags["ALBUM"]="TALB"
        tags["TRACKNUMBER"]="TRCK"
        tags["TITLE"]="TIT2"
        tags["DATE"]="TDRC"
        tags["GENRE"]="GENRE"
        tags["COMPOSER"]="TCOM"
        tags["CONDUCTOR"]="TPE3"
        tags["DISCNUMBER"]="TPOS"
        tags["TOTALDISCS"]="TXXX"
        tags["COMPILATION"]="TXXX"
        tags["WORK"]="TXXX"
        tags["FLAC_MD5"]="TXXX"
    
        if [[ -f $mp3 ]]; then
            local timestamp_mp3=$(stat -c "%Y" "$mp3")
            
            if [[ $timestamp_mp3 == $timestamp_flac ]]; then
                print_nv "[ ${green}UP-TO-DATE${text_reset} ]  $mp3"
                return
            elif [[ $timestamp_mp3 == $(( $timestamp_flac + 1 )) ]]; then
                print_nv "[ ${yellow}REPLAYGAIN MISSING${text_reset} ]  $mp3"
                echo "$mp3" >> $need_replaygain
                return
            else
                print_nv "[ ${yellow}CHANGED${text_reset} ]  $mp3"
                if [[ $(metaflac --show-md5sum "$flac") == $(eyeD3 -2 --no-config "$mp3" 2>&1 | grep -A 1 'FLAC_MD5' | tail -n 1) ]]; then
                    retag_only=true
                else
                    rm -f "$mp3"
                fi
            fi
        fi
    
        if $retag_only; then
            local replaygain_tags=$(eyeD3 -2 --no-config "$mp3" | grep -A 1 'replaygain_' | sed -r 's/^.*\s(r.*)\]$/--user-text-frame=\"\1:/g; s/([^|-][0-9\.]+).*/\1\" /g' | tr -d '\n')
            eyeD3 --remove-all --no-config "$mp3"
            tags_to_add+=($replaygain_tags)
        else
            set -o pipefail
            if ! sox -S "$flac" -t .wav -c 2 - rate -h 44100 | lame -V $vbr_quality --noreplaygain - "$mp3"; then
                print_nv "[ ${red}ERROR${text_reset} ]  Flac to mp3 encode failed for $flac"
                return
            fi
        fi
        
        local flac_tag=$(metaflac --export-tags-to=- "$flac")
    
        for tag in "${!tags[@]}"; do
            if [[ $tag == "FLAC_MD5" ]]; then
                tag_data=$(metaflac --show-md5sum "$flac")
            else
                tag_data=$(echo "$flac_tag" | sed -r 's/\$|`|\"/\\&/g' | awk 'tolower($0) !~ /^'"${tag,,}"'=/ {next} {sub(/.*=/, "", $0)} NF {if (x == "") x = $0; else x = x" / "$0} END {print x}')
            fi
    
            if [[ $tag_data == "" ]]; then
                continue
            fi
    
            if [[ ${tags[$tag]} == "GENRE" ]]; then
                tags_to_add+=("--genre=\"$tag_data\"")
            elif [[ ${tags[$tag]} == "TXXX" ]]; then
                tags_to_add+=("--user-text-frame=\"${tag}:$tag_data\"")
            else
                tags_to_add+=("--text-frame=\"${tags[$tag]}:$tag_data\"")
            fi
        done
    
        if [[ ${#tags_to_add[@]} -ne 0 ]]; then
            if eval eyeD3 ${tag_options[@]} ${tags_to_add[@]} "$(printf '%q' "$mp3")"; then
                if $retag_only; then
                    if [[ $(echo "$replaygain_tags" | grep -E -o 'replaygain_reference_loudness|replaygain_track_gain|replaygain_track_peak' | wc -l) -lt 3 ]]; then
                        echo "$mp3" >> $need_replaygain
                        touch -d @$(( $timestamp_flac + 1 )) "$mp3"
                    else
                        touch -d @$timestamp_flac "$mp3"
                    fi
                    print_nv "[ ${turquoise}RETAG OK${text_reset} ]  $mp3"
                else
                    echo "$mp3" >> $need_replaygain
                    touch -d @$(( $timestamp_flac + 1 )) "$mp3"
                    print_nv "[ ${turquoise}FLAC -> MP3 OK${text_reset} ]  $mp3"
                fi
            else
                print_nv "[ ${red}ERROR${text_reset} ]  Tagging failed for $mp3"
            fi
        fi
    }
    
    # Function that finds mp3 files with no flac equivalent
    remove_if_no_flac () {
        local valid_mp3_files=$(mktemp)
        local mp3_files=$(mktemp)
        
        cd "$musicdir"
        find . -type f -iname '*.flac' | sed -r 's/\..{4}$/.mp3/' > $valid_mp3_files
        cd "$mp3_destdir"
        find . -type f -name '*.mp3' > $mp3_files
        grep -FZvf $valid_mp3_files $mp3_files | xargs -0 -d "\n" -r -n 1 -P $concurrency bash -c 'delete_mp3 "$@"' --
        rm -f $valid_mp3_files $mp3_files
    }
    
    # Function that deletes a mp3 file. Expects a relative path.
    delete_mp3 () {
        local mp3="${mp3_destdir}${1#./}"
        print_nv "[ ${yellow}DELETE${text_reset} ]  $mp3"
        rm -f "$mp3"
    }
    
    # Function that calculates replay gain for a directory with mp3s while preserving timestamps
    add_replaygain () {
        local mp3_dir="$1"
        local timestamps=()
        local tag_options=($tag_options)
        
        cd "$mp3_dir"
        for mp3 in *.mp3; do
            local timestamp_mp3=$(stat -c "%Y" "$mp3")
            if ! grep -F "${mp3_dir}/${mp3}" $need_replaygain; then
                eyeD3 -2 --no-config --user-text-frame="replaygain_reference_loudness:" --user-text-frame="replaygain_track_gain:" --user-text-frame="replaygain_track_peak:" --user-text-frame="replaygain_album_gain:" --user-text-frame="replaygain_album_peak:" "$mp3" > /dev/null 2>&1
                timestamps+=($(( $timestamp_mp3 + 1 )))
            else
                timestamps+=($timestamp_mp3)
            fi
        done
    
        if ! mp3gain -s i *.mp3; then
            print_nv "[ ${red}REPLAYGAIN FAILED${text_reset} ]  $mp3_dir"
        fi
    
        # mp3gain always converts tags to id3v2.4 when using id3v2 tags. Process the tags using eyeD3 to enforce correct tag settings
        eyeD3 ${tag_options[@]} --remove-frame="RVA2" --user-text-frame="MP3GAIN_ALBUM_MINMAX:" --user-text-frame="MP3GAIN_MINMAX:" *.mp3 > /dev/null 2>&1
    
        local index=0
        for mp3 in *.mp3; do
            touch -d @$(( ${timestamps[$index]} - 1 )) "$mp3"
            print_nv "[ ${turquoise}REPLAYGAIN OK${text_reset} ]  ${mp3_dir}/${mp3}"
            index=$(( $index + 1 ))
        done
    }
    
    # Initialize
    export -f print_nv
    export -f verbosity
    export need_replaygain=$(mktemp)
    
    # Stage 1: Sync all files except flac-files while preventing deletion of mp3-files in the destination which obviously do not match any files in the source directory
    sync_library_structure "$musicdir" "$mp3_destdir"
    
    # Stage 2: Reencode all flac files to mp3
    trap 'abort' SIGINT SIGTERM
    export -f flac_to_mp3
    find "$musicdir" -type f -iname '*.flac' -print0 | xargs -0 -r -n 1 -P $concurrency bash -c 'flac_to_mp3 "$@"' --
    
    # Stage 3: Remove mp3s without a flac equivalent
    export -f delete_mp3
    remove_if_no_flac
    
    # Stage 4: Calculate replaygain for all files
    export -f add_replaygain
    cat $need_replaygain | sed 's/\/[^\/]*$//' | sort -u | xargs -r -d "\n" -n 1 -P $concurrency bash -c 'add_replaygain "$@"' --
    
    # Clean up
    rm -f $need_replaygain
    trap - SIGINT SIGTERM

  9. #9
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    Sitat Sitat fra Asbjørn Se Innlegg
    Hvilken del av FOR /R %%A IN (*.flac) DO er det du ikke forstår?
    Jeg høyner med denne fra mp3-mirror.sh:
    Kode:
    tag_data=$(echo "$flac_tag" | sed -r 's/\$|`|\"/\\&/g' | awk 'tolower($0) !~ /^'"${tag,,}"'=/ {next} {sub(/.*=/, "", $0)} NF {if (x == "") x = $0; else x = x" / "$0} END {print x}')
    Jeg har sett over denne forferdelige linjen igjen og igjen og dette representerer utrolig nok den optimaliserte versjonen. Ikke et eneste tegn kan fjernes uten at det blir tull. Det var når jeg strevde med dette at jeg skulle ønske jeg valgte python i stedet for bash, siden dette ville sett mye penere og mer forståelig ut der

  10. #10
    Æresmedlem Asbjørn's Avatar
    Ble medlem
    Mar 2006
    Innlegg
    21,686
    Tagget i
    42 Innlegg
    Interessant script. Jeg blir nesten nostalgisk, for jeg gjorde mye rart med sed og awk tidlig på 90-tallet. De litt større jobbene brukte jeg gjerne perl til. Blant annet lagde jeg meg en virtuell supercomputer i perl, hvor en Linux-maskin fordelte simuleringsjobber på et cluster av Sun arbeidsstasjoner og samlet sammen resultatene fra alle replikasjonene over nettverket. En såkalt "slopsucker", siden hver simulering kjørte i bakgrunnen med lav prioritet og var tilnærmet usynlig for den som eventuelt jobbet på maskinen.

    Jeg har også et script som vedlikeholder portable versjoner av flac-biblioteket, men det virker på litt annen måte. Jeg bruker 16 bits/44.1 kHz AAC som det portable formatet, ikke mp3. Så bruker jeg dBpoweramp's coreconverter.exe som motor i konverteringen, slik at jeg kan bruke de DSP-funksjonene som ligger der. Den kaller i sin tur Nero for selve konverteringen. Dessverre betyr dette at scriptet må kjøre på PC'en i stedet for på ReadyNAS'en, så det blir mye skyfling av data frem og tilbake over nettverket. Jeg har heller ikke lagd en funksjon for å slette uaktuelle AAC-filer, men gjør dette for hånd ved behov. Og så er det et python-script MakeAAC.py, ikke bash:

    Kode:
    #!/usr/bin/env python
    # -*- coding=latin_1 -*-
    
    import os
    import sys
    import re
    import string
    import codecs
    import subprocess
    import unicodedata
    import shutil
    from mutagen.flac import FLAC
    
    tgthead = "z:\media\AAC"
    badchars= re.compile(r'[^A-Za-z0-9_ ]+|^ | $|^$')
    
    def deScandify(s):
            # replace Scandinavian and German characters by ASCII versions
            s = re.sub("Æ", "AE", s)
            s = re.sub("æ", "ae", s)
            s = re.sub("Ø", "O", s)
            s = re.sub("ø", "o", s)
            s = re.sub("Å", "A", s)
            s = re.sub("å", "a", s)
            s = re.sub("Ö", "O", s)
            s = re.sub("ö", "o", s)
            return s
    
    filecounter = 0
    print "Scanning for new or changed files..."
    for root, dirs, files in os.walk("."):
    	for name in files:
    		if re.search("flac$", name):
                            filecounter += 1
                            sys.stdout.write(str.format("\r{0:0>6}", filecounter))
                            sys.stdout.flush()
                            
    			flacfilename = '{0}\{1}'.format(root, name)
    			audio = FLAC(flacfilename)
    
                            if "artist" in audio:
                                    artist = audio["artist"]
                                    sartist = unicodedata.normalize('NFKD', unicode(artist[0])).encode('latin-1', 'ignore')
                                    sartist = deScandify(sartist)
                                    sartist = badchars.sub("", sartist)
                            else:
                                    sartist = "Unknown"
    
    			if "albumartist" in audio:
                                    albumartist = audio["albumartist"]
                                    salbumartist = unicodedata.normalize('NFKD', unicode(albumartist[0])).encode('latin-1', 'ignore')
                                    salbumartist = deScandify(salbumartist)
                                    salbumartist = badchars.sub("", salbumartist)
                            else:
                                    salbumartist = sartist
                                    
                            if "compilation" in audio:
                                    compilation = audio["compilation"]
                                    scomp = unicodedata.normalize('NFKD', unicode(compilation[0])).encode('latin-1', 'ignore')
                                    if scomp == "1" and not "albumartist" in audio:
                                            salbumartist = "Various Artists"
    
    			if "album" in audio:
                                    album = audio["album"]
                                    salbum = unicodedata.normalize('NFKD', unicode(album[0])).encode('latin-1', 'ignore')
                                    salbum = deScandify(salbum)
                                    salbum = badchars.sub("", salbum)
                            else:
                                    salbum = "Unknown"
                                    
    			if "title" in audio:
                                    title = audio["title"]
                                    stitle = unicodedata.normalize('NFKD', unicode(title[0])).encode('latin-1', 'ignore')
                                    stitle = deScandify(stitle)
                                    stitle = badchars.sub("", stitle)
                            else:
                                    stitle = "Unknown"
    
    			if "tracknumber" in audio:
                                    track = audio["tracknumber"]
                                    strack = track[0].encode("latin-1", "ignore")
                                    strack = badchars.sub("", strack)
                            else:
                                    strack = "xx"
    
                            albumartistdir = os.path.join(tgthead, salbumartist)
    			albumartistdir = " ".join(albumartistdir.split())
                            if not os.path.exists(albumartistdir):
                                    os.mkdir(albumartistdir)
    
    			albumdir = os.path.join(albumartistdir, salbum)
    			albumdir = " ".join(albumdir.split())
                            if not os.path.exists(albumdir):
                                    os.mkdir(albumdir)
    
    			trackname = str.format("{0:0>2} - {1} - {2}.m4a", strack, sartist, stitle)
                            trackname = " ".join(trackname.split())
                            tgtpath = os.path.join(albumdir, trackname)
    
                            doconvert = False
                            if not os.path.exists(tgtpath):
                                    doconvert = True
                            else:
                                    srctime = os.path.getmtime(flacfilename)
                                    tgttime = os.path.getmtime(tgtpath)
                                    if srctime > tgttime:
                                            doconvert = True
    
                            if doconvert:
                                    sys.stdout.write("\n")
                                    tmpname = os.path.join("C:\TEMP",trackname)
    
                                    cmdline = str.format('"C:\\Programfiler\\Illustrate\\dBpoweramp\\coreconverter.exe" -infile=\"{0}\" -outfile=\"{1}\" -convert_to=\"m4a Nero (AAC)\" -dspeffect1=\"ReplayGain (Apply)= -mode={{qt}}1{{qt}} -noclac -activedb={{qt}}0{{qt}} -nonactivedb={{qt}}0{{qt}}\" -dspeffect2=\"Bit Depth=-depth={{qt}}16{{qt}}\" -dspeffect3=\"Resample=-frequency={{qt}}44100{{qt}}\" -cli_encoder=\"C:\Programfiler\Illustrate\dBpoweramp\encoder\m4a Nero (AAC)\neroAacEnc.exe\" -cli_cmd=\"-q 0.85 -ignorelength -if - -of {{qt}}[outfile]{{qt}}\"',\
                                              flacfilename, tmpname)
                                    subprocess.check_call(cmdline, shell=True)
                                    print "Copying to ", albumdir
                                    shutil.copy(tmpname, tgtpath)
                                    os.remove(tmpname)
                                    sys.stdout.write("\n")
                                    sys.stdout.flush()
    Mye av koden går med til å fjerne spesialtegn og sære bokstaver fra filnavnet. Scriptet viser også en løpende teller på skjermen for hvor mange filer den har sett på, sånn at det ikke ser ut som om det henger halve dagen.

    Av en eller annen grunn får ikke Nero med seg den ordren om "-q 0.85" (very high quality), så den konverterer til normale -q 0.45 (medium quality) i stedet. Det blir likevel forbausende bra. Jeg tror forklaringen på det er at coreconverter.exe først gjør en ReplayGain (Apply) og tilpasser nivået på hver fil i henhold til lagrede ReplayGain-tags før selve konverteringen skjer. Da får Nero mye headroom å jobbe i. De første AAC-filene jeg lagde låt litt hardt på iPhone, noe jeg trodde var en begrensning i hardware, men filene som kommer ut av dette scriptet har ikke den hardheten, såvidt jeg kan høre.

    Uansett, det scriptet kan kalles fra en wrapper i cmd, f eks MakeAAC.bat:

    REM Batch job to create AAC copies of FLAC files
    cd /d z:\media\Music
    C:\Python27\python.exe MakeAAC.py
    pause



    Så er det bare å klikke på ikonet for den batch-filen og la scriptet jobbe over natten.
    Siste redigert av Asbjørn; 03.06.2013 kl. 13:01.

  11. #11
    Hifi Freak Falcon1i's Avatar
    Ble medlem
    Sep 2012
    Sted
    Exil i huvudstaden
    Innlegg
    1,822
    Tagget i
    0 Innlegg
    Sitat Sitat fra marsboer Se Innlegg
    Sitat Sitat fra Asbjørn Se Innlegg
    Hvilken del av FOR /R %%A IN (*.flac) DO er det du ikke forstår?
    Jeg høyner med denne fra mp3-mirror.sh:
    Kode:
    tag_data=$(echo "$flac_tag" | sed -r 's/\$|`|\"/\\&/g' | awk 'tolower($0) !~ /^'"${tag,,}"'=/ {next} {sub(/.*=/, "", $0)} NF {if (x == "") x = $0; else x = x" / "$0} END {print x}')
    Jeg har sett over denne forferdelige linjen igjen og igjen og dette representerer utrolig nok den optimaliserte versjonen. Ikke et eneste tegn kan fjernes uten at det blir tull. Det var når jeg strevde med dette at jeg skulle ønske jeg valgte python i stedet for bash, siden dette ville sett mye penere og mer forståelig ut der
    Den där raden har jag slitit med också.
    Laddade ner ditt script för ett tag sedan med avsikt att köra det på min NAS, ReadyNAS Ultra. Lyckades installera alla beroenden
    efter en del pyssel men just den här raden i scriptet ville inte fungera i den version av bash som används i NAS:en. Försökte skriva
    om eftersom det kändes vanskligt att uppdatera bash. Lyckades aldrig få till scriptet till 100% så till slut gav jag mig på att uppdatera bash
    samt en del beroenden till detta. Resultatet blev att scriptet fungerade men NAS:en blev ostabil...inte helt optimalt. Troligen var jag lite
    oförsiktig vid något tillfälle med de sources jag hade för apt.

    Slutade med att jag fick installera om OS:et på nytt...no big-deal, går relativt fort och enkelt att få upp igen med bibehållna shares.

    Någon regnig dag i sommar skall jag ge mig på det igen
    "At least one music folder will be left on a music stand after each rehearsal.
    It will usually be the same player. If it is not the same player, there will be no name in the folder."

    Plinius | Audion Sterling | Casta Acoustics Model B | Harmony Design - DA9 | Sonos

  12. #12
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    Det kan være at NASen ble ustabil som en konsekvens av at både disk og CPU får virkelig juling når skriptet kjører. Supermicro-serveren min begynner å hyle som et skremt dyr når dette scriptet setter i gang, og da først og fremst på grunn av parallelliseringen som medfører at ingen del av serverens I/O system forblir upint.

    Uansett så har jeg aldri testet scriptet på en NAS. Avhengighetene er først og fremst tilpasset Debian Wheezy (dvs nåværende stable) og senere. NASer har jo ofte litt eldre og tilpasset programvare.
    Siste redigert av marsboer; 03.06.2013 kl. 10:36.

  13. #13
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    Fant du forøvrig ut hva som var problemet? Jeg bruker i hvertfall én såkalt "bashism" der i form av ${tag,,} som automatisk gjør $tag variabelen til lowercase uten å endre selve variabelen. Dette er ting som kan skrives om dersom dette gir scriptet høyere kompatibilitet med eldre bash-varianter. Hvis du klarer å peke konkret på hva som var problemet så kan jeg se på saken.

  14. #14
    Hifi Freak Falcon1i's Avatar
    Ble medlem
    Sep 2012
    Sted
    Exil i huvudstaden
    Innlegg
    1,822
    Tagget i
    0 Innlegg
    Det var just denna del, ${tag,,}, som var problemet. Höll på ett tag utan att få till något motsvarande som fungerade.
    ReadyNas Ultra kör ju en ganska gammal version, Debian etch, om jag inte minns fel.

    Att den blev ostabil märktes först i web-interfacet, inget svar från många dialoger.
    Hade på känn att den inte skulle klara en reboot ...vilket den inte gjorde när jag sedan testade.
    Fick en kernel-panic redan vid shutdown och bootade aldrig så långt att nät-interfacet gick upp och ingen console
    finns ju tillgänglig. Blev en OS-reinstall och det var inte första gången...en ReadyNas mår bäst av att inte 'pillas' med,
    men svårt att låta bli ibland
    "At least one music folder will be left on a music stand after each rehearsal.
    It will usually be the same player. If it is not the same player, there will be no name in the folder."

    Plinius | Audion Sterling | Casta Acoustics Model B | Harmony Design - DA9 | Sonos

  15. #15
    Hifi Freak marsboer's Avatar
    Ble medlem
    Apr 2010
    Sted
    Akershus
    Innlegg
    3,748
    Tagget i
    4 Innlegg
    OK. Det er sannsynlivis fullt løsbart å finne en erstatning for ${tag,,}, selv om det blir litt mer kode. Jeg skal sjekke det opp når jeg kommer hjem.

  16. #16
    Hifi Freak Falcon1i's Avatar
    Ble medlem
    Sep 2012
    Sted
    Exil i huvudstaden
    Innlegg
    1,822
    Tagget i
    0 Innlegg
    Sitat Sitat fra marsboer Se Innlegg
    OK. Det er sannsynlivis fullt løsbart å finne en erstatning for ${tag,,}, selv om det blir litt mer kode. Jeg skal sjekke det opp når jeg kommer hjem.
    Det är inget som brådskar Tänkte testa detta vidare när jag fått ny NAS i hus, då kan jag laborera med ReadyNas:en utan att bli
    sittandes utan musik vid ett eventuellt missöde. Även om det är hyfsat snabbt att komma på banan igen blir det ju ett avbrott.
    En hel del pyssel med 3-e parts repos och det gäller att hålla tungan i rätt mun

    Har inte bestämt mig riktigt än om vilken ny NAS jag skall välja. Kanske någon ur den nya generationens Readynas, alternativt bygga
    något eget baserat på FreeNas(mest för nöjet av att ha något eget )
    "At least one music folder will be left on a music stand after each rehearsal.
    It will usually be the same player. If it is not the same player, there will be no name in the folder."

    Plinius | Audion Sterling | Casta Acoustics Model B | Harmony Design - DA9 | Sonos

  17. #17
    Hifi Freak kanaris's Avatar
    Ble medlem
    Mar 2010
    Sted
    Fetsund
    Innlegg
    1,755
    Tagget i
    0 Innlegg
    Noen her som vet om en måte å generere en utskrivbar liste av biblioteket.

    Tenker meg noe som Artist - Album - År.

    Bruker Jriver og Squeezebox hvis det har noe å si. I tillegg er jeg grønn på data


    mvh
    ​jRiver/Thorens TD 320- Beresford Caiman/Jasmine LP-2.5DU - Musical Innovation MI1 -
    Musical Innovation MI18 - Klipsch RF5

  18. #18
    Hifi Freak Falcon1i's Avatar
    Ble medlem
    Sep 2012
    Sted
    Exil i huvudstaden
    Innlegg
    1,822
    Tagget i
    0 Innlegg
    Sitat Sitat fra kanaris Se Innlegg
    Noen her som vet om en måte å generere en utskrivbar liste av biblioteket.

    Tenker meg noe som Artist - Album - År.

    Bruker Jriver og Squeezebox hvis det har noe å si. I tillegg er jeg grønn på data


    mvh
    Har inte sett att Jriver har något verktyg/plugin för det.
    Jag använder mp3tag(freeware) och med det kan man exportera till olika format, t.ex. csv, html. Man kan också
    välja vilken information som skall exporteras.
    "At least one music folder will be left on a music stand after each rehearsal.
    It will usually be the same player. If it is not the same player, there will be no name in the folder."

    Plinius | Audion Sterling | Casta Acoustics Model B | Harmony Design - DA9 | Sonos

  19. #19
    Hifi Freak kanaris's Avatar
    Ble medlem
    Mar 2010
    Sted
    Fetsund
    Innlegg
    1,755
    Tagget i
    0 Innlegg
    Sitat Sitat fra Falcon1i Se Innlegg
    Sitat Sitat fra kanaris Se Innlegg
    Noen her som vet om en måte å generere en utskrivbar liste av biblioteket.

    Tenker meg noe som Artist - Album - År.

    Bruker Jriver og Squeezebox hvis det har noe å si. I tillegg er jeg grønn på data


    mvh
    Har inte sett att Jriver har något verktyg/plugin för det.
    Jag använder mp3tag(freeware) och med det kan man exportera till olika format, t.ex. csv, html. Man kan också
    välja vilken information som skall exporteras.
    Har jo mp3tag. Da får jeg sjekke den

    Takker for svar.


    mvh
    ​jRiver/Thorens TD 320- Beresford Caiman/Jasmine LP-2.5DU - Musical Innovation MI1 -
    Musical Innovation MI18 - Klipsch RF5

  20. #20
    Æresmedlem Asbjørn's Avatar
    Ble medlem
    Mar 2006
    Innlegg
    21,686
    Tagget i
    42 Innlegg
    Ja, mp3tag har en "Export..."-kommando på filmenyen. Den bruker et filformat som heter .mte for å definere eksakt hva som skal eksporteres og i hvilket format. Det er nok den mest brukervennlige måten. Men det kan være nokså tidkrevende å åpne hele biblioteket samtidig i mp3tag bare for å kunne eksportere en liste med artist, album, år og tittel for hver sang, så jeg skrev et lite python-script for dette også. Da vil jeg også ha med ReplayGain-tag'ene for hver sang. Scriptet scanflac.py ser slik ut:

    Kode:
    #!/usr/bin/env python
    # -*- coding=latin_1 -*-
    
    import os
    import re
    import string
    import codecs
    from mutagen.flac import FLAC
    
    print "flacfile\tartist\talbum\tgenre\tdate\treplaygain_album_gain\treplaygain_album_peak\ttitle\treplaygain_track_gain\treplaygain_track_peak"
    
    for root, dirs, files in os.walk("."):
    	for name in files:
    		if re.search("flac$", name):
    			flacfilename = '{0}\{1}'.format(root, name)
    			audio = FLAC(flacfilename)
    			
    			if "artist" in audio:
                                    artist = audio["artist"]
                                    sartist = artist[0].encode("latin_1", "ignore")
                            else:
                                    sartist = "Unknown"
                                    
    			if "album" in audio:
                                    album = audio["album"]
                                    salbum = album[0].encode("latin_1", "ignore")
                            else:
                                    salbum = "Unknown"
                                    
    			if "genre" in audio:
                                    genre = audio["Genre"]
                                    sgenre = genre[0].encode("latin_1", "ignore")
                            else:
                                    sgenre = "Unknown"
                                    
    			if "date" in audio:
                                    date = audio["date"]
                                    sdate = date[0].encode("latin_1", "ignore")
                            else:
                                    sdate = "Unknown"
                            
    			if "replaygain_album_gain" in audio:
                                    rgag = audio["replaygain_album_gain"]
                                    srgag = re.sub(" dB", "", rgag[0])
                            else:
                                    srgag = "Unknown"
                                    
    			if "replaygain_album_peak" in audio:
                                    rgap = audio["replaygain_album_peak"]
                                    srgap = re.sub(" dB", "", rgap[0])
                            else:
                                    srgap = "Unknown"
                             
    			if "title" in audio:
                                    title = audio["title"]
                                    stitle = title[0].encode("latin_1", "ignore")
                            else:
                                    stitle = "Unknown"
    
    			if "replaygain_track_gain" in audio:
                                    rgtg = audio["replaygain_track_gain"]
                                    srgtg = re.sub(" dB", "", rgtg[0])
                            else:
                                    srgtg = "Unknown"
                                    
    			if "replaygain_track_peak" in audio:
                                    rgtp = audio["replaygain_track_peak"]
                                    srgtp = rgtp[0].encode("latin_1", "ignore")
                            else:
                                    srgtp = "Unknown"
    			
    			print flacfilename, '\t', sartist, '\t', \
                                  salbum, '\t', sgenre, '\t', sdate, '\t', \
                                  srgag, '\t', srgap, '\t', stitle, '\t', \
                                  srgtg, '\t', srgtp
    Ganske enkelt, egentlig. Scriptet skriver ut en linje med kolonneoverskrifter. Deretter rusler det gjennom hele mappestrukturen og ser på hver enkelt flac-fil. Det plukker ut info fra tags og formatterer dem som ryddige tekststrenger eller setter inn ordet "Unknown" hvis den tag-verdien ikke finnes. For hver fil skriver den ut en linje med filnavn, artist, album, genre, år, album gain, album peak, track, track gain og track peak. De enkelte dataverdiene er skilt med tabs på hver linje.

    Det scriptet kan også puttes inn i en wrapper som det forrige, en enkel batchjobb (cmd) som starter det og redirigerer output til en fil. Deretter kan den tekstfilen importeres i Excel og sorteres på alle tenkelige måter. Dessuten kan man lage diverse grafer, som for eksempel denne med anslått dynamikk for 40000 sanger sortert etter årstall:

    Navn:      Microsoft Excel - scanflac.xls_4.jpg
Visninger: 465
Størrelse: 106.5 Kb

    Joda, den gjennomsnittlige nyutgitte CD'en har en god del mindre dynamikk enn hva som fantes på de gamle 78-steinkakene fra før microgroove LP ble oppfunnet i 1948...

    En annen ting man kan gjøre med den filen er å opprette pivottabeller i Excel og lære ting om musikksamlingen sin man kanskje ikke visste. Jeg ser at jeg har flest spor med Bob Dylan (563), etterfulgt av Rolling Stones (551), The Beatles (47 og Bruce Springsteen (471). Derimot er det ca 2100 artister med bare ett spor hver. En del av dem er nok sporadiske stavefeil i artistnavn, men likevel var det ganske mange.
    Siste redigert av Asbjørn; 03.06.2013 kl. 22:22.

Side 1 av 3 123 SisteSiste

Tags for denne Tråden

Skrive Tillatelser

  • Du kan ikke starte nye tråder
  • Du kan ikke svare på innlegg
  • Du kan ikke laste opp vedlegg
  • Du kan ikke redigere dine innlegg
  •  


 

Om Hifisentralen

    Hifisentralen er Norges største webside innen high-end hi-fi og musikk, og vi har vært på nett siden år 2001. Velkommen til en god hi-fi diskusjon eller kjøp og salg av utstyr.
   

Følg oss på sosiale medier:

Facebook Twitter RSS Feed