3 mensajes Página 1 de 1
Bueno, el título esta bastante largo pero ahí vamos. Una parte de este script es una adaptación de otro que hay para linux pero sólo en las funciones que leen los id3tag y extraen la imagen del APIC tag. El resto sno funciones hechas por mí como la que es para calcular el SafeSynch Integer en base a un integer normal y viceversa.

Agradezco a Diaelectronics por su script para convertir de hexadecimal a binario (y viceversa) ya que sin ese script no hubiera podido hacer el resto de las funciones.

El script se puede adaptar para leer y editar el resto de los tags v2.3 ó incluso añadirlos, leer los flag bytes, etc. y es por ello que dejo siempre el código fuente.

Aca el script completo con algunas modificaciones como la de insertar el APIC en caso de no encontrarlo, ó cambiar el APIC en caso de contrario.

Script:
--[[Mp3 id3 v2.3 tag reader APIC Extract/edit/insert - By [email protected]

Changelog v0.9.0.0:
        - Added function id3.readAPICInfo: Returns available info of the APIC frame and can extract the APIC picture
        - Improved function id3.changeArt: Added PNG support (omits png's CRC), description and much more;
        - Fixed function id3.changeArt: Was deleting the mp3 data if a png image was found and removed unnecessary code
        - Fixed function id3.getArt: Was failing while extracting png files

NOTES:
        - This code won't work with unicode data for now
        - id3.getArt and id3.readAPICInfo functions extract the APIC picture file in different ways
]]
--
id3 = {};
id3.version = "0.9.0.0";
function id3.getID3Version(mp3)
        local sF = io.open(mp3, "rb");
        if sF then
                local data = sF:read(4);
                sF:close();
                if data:sub(1,3) == "ID3" then
                        return string.byte(data:sub(4));
                else
                        return nil, "Invalid ID3 v2 TAG";
                end
        else
                return nil, "File not found";
        end
end
function id3.getV1(mp3)
--Thnx to: http://psp.scenebeta.com/node/70241
        local name,artist,album,track,genre,year,comment = ""
        local tGenre = {};
        tGenre[0] = "Blues";
        tGenre[1] = "Classic Rock";
        tGenre[2] = "Country";
        tGenre[3] = "Dance";
        tGenre[4] = "Disco";
        tGenre[5] = "Funk";
        tGenre[6] = "Grunge";
        tGenre[7] = "Hip-Hop";
        tGenre[8] = "Jazz";
        tGenre[9] = "Metal";
        tGenre[10] = "New Age";
        tGenre[11] = "Oldies";
        tGenre[12] = "Other";
        tGenre[13] = "Pop";
        tGenre[14] = "R&B";
        tGenre[15] = "Rap";
        tGenre[16] = "Reggae";
        tGenre[17] = "Rock";
        tGenre[18] = "Techno";
        tGenre[19] = "Industrial";
        tGenre[20] = "Alternative";
        tGenre[21] = "Ska";
        tGenre[22] = "Death Metal";
        tGenre[23] = "Pranks";
        tGenre[24] = "Soundtrack";
        tGenre[25] = "Euro-Techno";
        tGenre[26] = "Ambient";
        tGenre[27] = "Trip-Hop";
        tGenre[28] = "Vocal";
        tGenre[29] = "Jazz+Funk";
        tGenre[30] = "Fusion";
        tGenre[31] = "Trance";
        tGenre[32] = "Classical";
        tGenre[33] = "Instrumental";
        tGenre[34] = "Acid";
        tGenre[35] = "House";
        tGenre[36] = "Game";
        tGenre[37] = "Sound Clip";
        tGenre[38] = "Gospel";
        tGenre[39] = "Noise";
        tGenre[40] = "Alternative Rock";
        tGenre[41] = "Bass";
        tGenre[43] = "Punk";
        tGenre[44] = "Space";
        tGenre[45] = "Meditative";
        tGenre[46] = "Instrumental Pop";
        tGenre[47] = "Instrumental Rock";
        tGenre[48] = "Ethnic";
        tGenre[49] = "Gothic";
        tGenre[50] = "Darkwave";
        tGenre[51] = "Techno-Industrial";
        tGenre[52] = "Electronic";
        tGenre[53] = "Pop-Folk";
        tGenre[54] = "Eurodance";
        tGenre[55] = "Dream";
        tGenre[56] = "Southern Rock";
        tGenre[57] = "Comedy";
        tGenre[58] = "Cult";
        tGenre[59] = "Gangsta";
        tGenre[60] = "Top 40";
        tGenre[61] = "Christian Rap";
        tGenre[62] = "Pop/Funk";
        tGenre[63] = "Jungle";
        tGenre[64] = "Native US";
        tGenre[65] = "Cabaret";
        tGenre[66] = "New Wave";
        tGenre[67] = "Psychadelic";
        tGenre[68] = "Rave";
        tGenre[69] = "Showtunes";
        tGenre[70] = "Trailer";
        tGenre[71] = "Lo-Fi";
        tGenre[72] = "Tribal";
        tGenre[73] = "Acid Punk";
        tGenre[74] = "Acid Jazz";
        tGenre[75] = "Polka";
        tGenre[76] = "Retro";
        tGenre[77] = "Musical";
        tGenre[78] = "Rock & Roll";
        tGenre[79] = "Hard Rock";
        tGenre[80] = "Folk";
        tGenre[81] = "Folk-Rock";
        tGenre[82] = "National Folk";
        tGenre[83] = "Swing";
        tGenre[84] = "Fast Fusion";
        tGenre[85] = "Bebob";
        tGenre[86] = "Latin";
        tGenre[87] = "Revival";
        tGenre[88] = "Celtic";
        tGenre[89] = "Bluegrass";
        tGenre[90] = "Avantgarde";
        tGenre[91] = "Gothic Rock";
        tGenre[92] = "Progressive Rock";
        tGenre[93] = "Psychedelic Rock";
        tGenre[94] = "Symphonic Rock";
        tGenre[95] = "Slow Rock";
        tGenre[96] = "Big Band";
        tGenre[97] = "Chorus";
        tGenre[98] = "Easy Listening";
        tGenre[99] = "Acoustic 100 - Humour";
        tGenre[101] = "Speech";
        tGenre[102] = "Chanson";
        tGenre[103] = "Opera";
        tGenre[104] = "Chamber Music";
        tGenre[105] = "Sonata";
        tGenre[106] = "Symphony";
        tGenre[107] = "Booty Bass";
        tGenre[108] = "Primus";
        tGenre[109] = "Porn Groove";
        tGenre[110] = "Satire";
        tGenre[111] = "Slow Jam";
        tGenre[112] = "Club";
        tGenre[113] = "Tango";
        tGenre[114] = "Samba";
        tGenre[115] = "Folklore";
        tGenre[116] = "Ballad";
        tGenre[117] = "Power Ballad";
        tGenre[118] = "Rhytmic Soul";
        tGenre[119] = "Freestyle";
        tGenre[120] = "Duet";
        tGenre[121] = "Punk Rock";
        tGenre[122] = "Drum Solo";
        tGenre[123] = "Acapella";
        tGenre[124] = "Euro-House";
        tGenre[125] = "Dance Hall";
        tGenre[126] = "Goa";
        tGenre[127] = "Drum & Bass";
        tGenre[128] = "Club-House";
        tGenre[129] = "Hardcore";
        tGenre[130] = "Terror";
        tGenre[131] = "Indie";
        tGenre[132] = "BritPop";
        tGenre[133] = "Negerpunk";
        tGenre[134] = "Polsk Punk";
        tGenre[135] = "Beat";
        tGenre[136] = "Christian Gangsta";
        tGenre[137] = "Heavy Metal";
        tGenre[138] = "Black Metal";
        tGenre[139] = "Crossover";
        tGenre[140] = "Contemporary C";
        tGenre[141] = "Christian Rock";
        tGenre[142] = "Merengue";
        tGenre[143] = "Salsa";
        tGenre[144] = "Thrash Metal";
        tGenre[145] = "Anime";
        tGenre[146] = "JPop";
        tGenre[147] = "SynthPop";
        tGenre[255] = "Not defined";
        local fbuf = io.open(mp3,"rb");
        if fbuf then
                fbuf:seek("end",-128);
                local tag = fbuf:read(3);
                if tag == "TAG" then
                        fbuf:seek("end",-125);
                        name = fbuf:read(30);
                        fbuf:seek("end",-95);
                        artist = fbuf:read(30);
                        fbuf:seek("end",-65);
                        album = fbuf:read(30);
                        fbuf:seek("end",-35);
                        year = fbuf:read(4);
                        fbuf:seek("end",-31);
                        comment = fbuf:read(28);
                        fbuf:seek("end",-2);
                        track = string.byte(fbuf:read(1));
                        fbuf:seek("end",-1);
                        genre = tGenre[string.byte(fbuf:read(1))];
                        for i = 1, string.len(name) do
                                if string.byte(string.sub(name,-1)) == 0 or string.byte(string.sub(name,-1)) == 32 then
                                        name = string.sub(name,1,-2);
                                end
                        end
                        for i = 1, string.len(artist) do
                                if string.byte(string.sub(artist,-1)) == 0 or string.byte(string.sub(artist,-1)) == 32 then
                                        artist = string.sub(artist,1,-2);
                                end
                        end
                        for i = 1, string.len(album) do
                                if string.byte(string.sub(album,-1)) == 0 or string.byte(string.sub(album,-1)) == 32 then
                                        album = string.sub(album,1,-2);
                                end
                        end
                end
                fbuf:close();
                return { name = name, artist = artist, album = album, track = track, genre = genre, year = year, comment = comment };
        else
                return nil;
        end
end

function id3.getV2(mp3,tag)
--Thnx to: http://psp.scenebeta.com/node/70241
        local fbuf = io.open(mp3,"rb")
        local ttag = ""
        if fbuf then
                fbuf:seek("set")
                local fdata = fbuf:read(100000)
                fbuf:close()
        end
        if fdata then
                local fndid = string.find(fdata, tag)
        end
        if fndid then
                ttag = string.sub(fdata, fndid + 11, fndid + 9 + string.byte(string.sub(fdata, fndid+7, fndid+7)))
                ttag = string.gsub(ttag, "\0", "")
        end
        return ttag
end

function id3.getArt(mp3, dest_file)
--Thnx to: http://psp.scenebeta.com/node/70241
        local fbuf = io.open(mp3,"rb");
        if fbuf then
                fbuf:seek("set");
                local fdata = fbuf:read("*a");
                local fndid = string.find(fdata, "APIC");
                if fndid then
                        local beg = string.sub(fdata,fndid);
                        if string.find(beg, "JFIF") then
                                local beg_pos = string.find(beg, "JFIF")-6;
                                beg = string.sub(beg,beg_pos)
                                local fin = string.find(beg, string.char(255)..string.char(217));
                                if fin then
                                        fin = fin + 1;
                                        img = string.sub(beg,1,fin);
                                        fbuf:close()
                                        local file = io.open(dest_file..".jpg","wb");
                                        file:write(img);
                                        file:close();
                                        return true;
                                else
                                        return nil, "JPG data not found";
                                end
                        else
                                local beg_pos = string.find(beg, "‰PNG")
                                if beg_pos then
                                        beg = string.sub(beg,beg_pos);
                                        local fin = string.find(beg, "IEND")+7;
                                        img = string.sub(beg,1,fin);
                                        fbuf:close();
                                        local file = io.open(dest_file..".png","wb");
                                        file:write(img);
                                        file:close();
                                        return true;
                                else
                                        return nil, "File format not found";
                                end
                        end
                else
                        return nil, "Couldn't find album art image";
                end
        else
                return nil, "Couldn't open mp3 file";
        end
end

function id3.changeArt(mp3, newIMG, desc)
--[[Insert or replace an APIC header
boolean id3.changeArt(string MP3File, string ImageFileToInsert, string ImageDescription);
Thnx to: MEEEEE ([email protected]) lol]]
--
        local sImg = (type(newIMG) == "string") and newIMG or "";
        local _,_,mime = sImg:find("%.+(.+)")
        if not mime then
                return nil, "Invalid IMG";
        else
                mime = mime:lower();
                if mime ~= "jpg" and mime ~= "png" and mime ~= "jpeg" then
                        return nil, "Image must be png or jpg";
                elseif mime == "jpg" then
                        mime = "jpeg";
                end
        end
        local sF = io.open(mp3, "rb");
        if sF then
                local data = sF:read("*a");
                sF:close();
                if data:sub(1,3) == "ID3" then
                        local tag_size = data:sub(7,10);
                        local n = "";
                        for x in tag_size:gmatch(".") do
                                local tmp = id3.Hex2Bin(string.format("%02X", x:byte()));
                                n = n..tmp:sub(2);
                        end
                        local d = string.format("%032s", n);
                        local size = tonumber("0x"..id3.Bin2Hex(d))+10;
                        local id3_data = data:sub(1, size);
                        local apic = string.find(id3_data, "APIC");
                        local img = io.open(newIMG, "rb");
                        if not img then
                                return nil, "New ART image not found";
                        end
                        local head_data = string.sub(id3_data, 1, 6);

                        --APIC Frame
                        local text_encoding = string.char(0);
                        local mime_type = "image/"..mime..string.char(0);
                        local pic_type = string.char(3);--Default: Front cover
                        local description = (type(desc)=="string") and desc:sub(1,63)..string.char(0) or string.char(0);
                        local img_data = img:read("*a");

                        --Check png format
                        if mime == "png" then
                                if img_data:find("IEND") then
                                        local s,_,v = img_data:find("IEND(.+)");
                                        if not v then
                                                return nil, "Invalid PNG file";
                                        elseif #v > 4 then
                                                img_data = img_data:sub(1, s+7);
                                        elseif #v < 4 then
                                                return nil, "Invalid PNG file, must end with 4 bytes after the IEND chunk";
                                        end
                                end
                        end

                        --APIC Header
                        local apic_head = "APIC";
                        local img_size = string.format("%08X", #text_encoding + #mime_type + #pic_type + #description + #img_data);
                        local img_sdata = "";
                        for s in string.gmatch(img_size , "..") do
                                img_sdata = img_sdata..string.char(tonumber("0x"..s));
                        end
                        local apic_flag = string.char(0,0);
                        img:close();

                        --APIC complete tag
                        local apic_data = apic_head..img_sdata..apic_flag..text_encoding..mime_type..pic_type..description..img_data;
                        if apic then
                                local fin = string.find(id3_data, string.char(255)..string.char(217));
                                if fin then
                                        fin = fin+1;
                                elseif string.find(id3_tag, "IEND") then
                                        fin = string.find(id3_data, "IEND")+7;
                                else
                                        return nil, "Invalid APIC TAG";
                                end
                                local prev_data = string.sub(id3_data, 11, apic-1);
                                local post_data = string.sub(id3_data, fin+1);
                                local new_adata = prev_data..apic_data..post_data;
                                local new_size = id3.IntToSynchSafe(#new_adata);
                                local  sO = io.open(mp3, "wb");
                                if sO then
                                        sO:write(head_data..new_size..new_adata..string.sub(data, size+1));
                                        sO:close();
                                        return true, "APIC edited";
                                else
                                        return nil, "Couldn't reopen file";
                                end
                        else
                                local post_data = string.sub(id3_data, 11);
                                local new_adata = apic_data..post_data;
                                local new_size = id3.IntToSynchSafe(#new_adata);
                                local  sO = io.open(mp3, "wb");
                                if sO then
                                        sO:write(head_data..new_size..new_adata..string.sub(data, size+1));
                                        sO:close();
                                        return true, "APIC inserted";
                                else
                                        return nil, "Couldn't reopen file";
                                end
                        end
                else
                        return nil, "ID3 v2 TAG not found";
                end
        end
end

function id3.getID3v23Size(mp3file)
--[[Returns the ID3 v23 TAG Size
More info: http://www.id3.org/id3v2.3.0]]
--
        local sF = io.open(mp3file, "rb");
        if sF then
                local data = sF:read(10);
                sF:close();
                if data:sub(1,3) == "ID3" then
                        local tag_size = data:sub(7,10);
                        local n = "";
                        for x in tag_size:gmatch(".") do
                                local tmp = id3.Hex2Bin(string.format("%02X", x:byte()));
                                n = n..tmp:sub(2);
                        end
                        local d = string.format("%032s", n);
                        return tonumber("0x"..id3.Bin2Hex(d))+10;
                else
                        return nil, "ID3 v2 TAG not found";
                end
        else
                return nil, "File not found";
        end
end

function id3.IntToSynchSafe(integer)
--[[Returns the integer converted into a SynchSafe (4 bytes) string + the new integer value (just to check) + the hex value;
More info: http://en.wikipedia.org/wiki/Synchsafe]]
--
        assert(type(integer)=="number", "Value must be a number");
        local hex = string.format("%08x",integer);
        local nhex = id3.Hex2Bin(hex);
        local t,i = {},4;
        for x in string.gmatch(nhex, "........") do
                t[i] = x;
                i = i-1;
        end
        local newbin, sres, nt= "", "", {};
        for x, y in pairs(t) do
                local a, c = y:sub(1,x), y:sub(x+1);
                table.insert(nt, a);
                newbin = "0"..c..(nt[x-1] or "")..newbin;
        end
        local newhex = id3.Bin2Hex(newbin);
        for sss in string.gmatch(newhex, "..") do
                sres = sres..string.char(tonumber("0x"..sss));
        end
        return sres, tonumber("0x"..newhex), "0x"..newhex;
end

function id3.Hex2Bin(s)
--[[Thanks to Diaelectronics for the hex2bin and bin2hex functions
More functions and module: http://www.dialectronics.com/Lua/code/BinDecHex.shtml]]
--
        local hex2bin = {
                ["0"] = "0000",
                ["1"] = "0001",
                ["2"] = "0010",
                ["3"] = "0011",
                ["4"] = "0100",
                ["5"] = "0101",
                ["6"] = "0110",
                ["7"] = "0111",
                ["8"] = "1000",
                ["9"] = "1001",
                ["a"] = "1010",
                ["b"] = "1011",
                ["c"] = "1100",
                ["d"] = "1101",
                ["e"] = "1110",
                ["f"] = "1111"
        };
        local ret = ""
        local i = 0
        for i in string.gfind(s, ".") do
                i = string.lower(i)
                ret = ret..hex2bin[i]
        end
        return ret
end

function id3.Bin2Hex(s)
        local bin2hex = {
                ["0000"] = "0",
                ["0001"] = "1",
                ["0010"] = "2",
                ["0011"] = "3",
                ["0100"] = "4",
                ["0101"] = "5",
                ["0110"] = "6",
                ["0111"] = "7",
                ["1000"] = "8",
                ["1001"] = "9",
                ["1010"] = "A",
                ["1011"] = "B",
                ["1100"] = "C",
                ["1101"] = "D",
                ["1110"] = "E",
                ["1111"] = "F"
        };
        local l = 0
        local h = ""
        local b = ""
        local rem
        l = string.len(s)
        rem = l % 4
        l = l-1
        h = ""
        if (rem > 0) then
                s = string.rep("0", 4 - rem)..s
        end
        for i = 1, l, 4 do
                b = string.sub(s, i, i+3);
                h = h..bin2hex[b];
        end
        return h;
end
function id3.readAPICInfo(mp3, writeAPIC)
--[[Reads the available info of the APIC frame
table id3.readAPICInfo(string MP3File, boolean WriteImageFileToTempFile);
Thnx to MEEEEE ([email protected]) lol]]
--
        local tTypes = {};
        tTypes[0] = "Other"
        tTypes[1] = "Icon";--32x32px file icon (PNG only)
        tTypes[2] = "Other icon file";
        tTypes[3] = "Front cover";
        tTypes[4] = "Back conver";
        tTypes[5] = "Leaflet";
        tTypes[6] = "Media";
        tTypes[7] = "Lead artist";
        tTypes[8] = "Artist";
        tTypes[9] = "Conductor";
        tTypes[10] = "Band";
        tTypes[11] = "Composer";
        tTypes[12] = "Lyricist";
        tTypes[13] = "Recording location";
        tTypes[14] = "During recording";
        tTypes[15] = "During performance";
        tTypes[16] = "Video capture";
        tTypes[17] = "Bright colored fish";
        tTypes[18] = "Illustration";
        tTypes[19] = "Band logotype";
        tTypes[20] = "Publisher logotype";
        local wa = (type(writeAPIC)=="boolean") and writeAPIC or false;
        local sF = io.open(mp3, "rb");
        if sF then
                local sData = sF:read("*a");
                sF:close();
                if sData then
                        --APIC HEADER
                        local start = sData:find("APIC");
                        if start then
                                local sized = sData:sub(start+4, start+7);
                                local size = "";
                                for x in sized:gmatch(".") do
                                        size = size..string.format("%X", x:byte());
                                end
                                size = tonumber("0x"..size);
                                local flags = sData:sub(start+8, start+9);

                                --APIC FRAME
                                local text_encoding = sData:sub(start+10, start+10);
                                local mimee = sData:find("%z", start+11);
                                local mime = sData:sub(start+11, mimee-1);
                                local pic_type = string.byte(sData:sub(mimee+1, mimee+1));
                                local desc_end = sData:find("%z", mimee+2);
                                local description = sData:sub(mimee+2, desc_end-1);
                                description = (#description == 0) and "" or description
                                local pic_data = sData:sub(desc_end+1, desc_end+1+(size-(1+#mime+1+1+#description+2)));
                                if wa then
                                        local f = io.open("temp."..mime, "wb");
                                        if f then
                                                f:write(pic_data);
                                                f:close();
                                        end
                                end
                                return {APICFrameSize = size, ImageSize=#pic_data, MimeType=mime, PictureType=tTypes[pic_type], description=description};
                        else
                                return nil, "APIC header not found";
                        end
                end
        end
end
 
Parsed in 0.063 seconds, using GeSHi 1.0.8.9


Script en Pastebin
http://pastebin.com/TTEaCGME

Adjunto una imagen donde se aprecia un mp3 cualquiera con un nuevo APIC que es un sí una captura de pantalla de un script lua.

Fuente: http://webultra.blogspot.com/2011/11/lua-id3-v23-tag-reader-apic-extractor.html
Publicado por Webultra en 01:47
Este código ( o parte) es el que use para el plugin de id3tag , lo encontré por hay y webultra lo mejoro como siempre , gracias makina
Very  Good  Work
Imagen
3 mensajes Página 1 de 1

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado