diff --git a/README.md b/README.md index fd52688..5b68a00 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ # HuffPress - + ## What's up? -Hey! This is a simple web-service called ![HuffPress](https://img.shields.io/badge/Huff-Press-orange.svg). What it does? Well, actually it *huffpresses* files... I mean, compresses files using the [Huffman compression](https://en.wikipedia.org/wiki/Huffman_coding "Read about it on Wikipedia"). +Hey! This is a simple web-service called ![HuffPress](https://img.shields.io/badge/Huff-Press-orange.svg). What it does? Well, actually it _huffpresses_ files... I mean, compresses files using the [Huffman compression](https://en.wikipedia.org/wiki/Huffman_coding 'Read about it on Wikipedia'). ## What do I need? - Any web browser - ![PHP 7+](https://img.shields.io/badge/PHP-7+-blueviolet.svg) -- ![Python 3+](https://img.shields.io/badge/Python-3+-blue.svg) +- ![Python 3.7+](https://img.shields.io/badge/Python-3+-blue.svg) - `click` library ## So, how do I use it? -I think it's too easy to explain the usage of this service, but anyway here's [a video guide](https://yadi.sk/i/aiy78bDaoKqoJQ "Watch!"). +I think it's too easy to explain the usage of this service, but anyway here's [a video guide](https://yadi.sk/i/aiy78bDaoKqoJQ 'Watch!'). diff --git a/huffman.py b/huffman.py index 90e6818..676e7ed 100644 --- a/huffman.py +++ b/huffman.py @@ -1,190 +1,189 @@ -from os.path import dirname,basename,join,abspath as path +from os.path import dirname, basename, join, abspath as path from time import time from datetime import datetime as dt import click + class Log: - def __init__(self,path,name): - self.path=path - self.name=name + def __init__(self, path, name): + self.path = path + self.name = name - def log(self,msg,name=None): - if name is None: - name=self.name - now=dt.today() - with open(self.path,'a',encoding='utf-8') as file: - file.write(f'{now.year}-{str(now.month).rjust(2,"0")}-{str(now.day).rjust(2,"0")} ' - f'{str(now.hour).rjust(2,"0")}:{str(now.minute).rjust(2,"0")}:{str(now.second).rjust(2,"0")},{str(now.microsecond)[:3]}' - ' | ' - f'{name}' - ' | ' - f'{msg}\n') + def log(self, msg, name=None): + if name is None: + name = self.name + now = dt.today() + with open(self.path, 'a', encoding='utf-8') as file: + file.write(f'{now.year}-{str(now.month).rjust(2, "0")}-{str(now.day).rjust(2, "0")} ' + f'{str(now.hour).rjust(2, "0")}:{str(now.minute).rjust(2, "0")}:{str(now.second).rjust(2, "0")},{str(now.microsecond)[:3]}' + ' | ' + f'{name}' + ' | ' + f'{msg}\n') -log=Log(join(dirname(__file__),'hfm.log'),'hfm-py') + +log = Log(join(dirname(__file__), 'hfm.log'), 'hfm-py') def huffman(data): - units={} #getting element-wise info - for c in data: - if c in units: - units[c]+=1 - else: - units[c]=1 - units,codes=sorted([([u],units[u]) for u in units],key=lambda u:u[1]),dict.fromkeys(units.keys(),'') - - while units: #creating Haffman table - if len(units)>2: - b=int(units[0][1]+units[1][1]>units[1][1]+units[2][1]) - else: - b=0 - for c in units[b][0]: - codes[c]='0'+codes[c] - for c in units[1+b][0]: - codes[c]='1'+codes[c] - units[2*b]=units[b][0]+units[1+b][0],units[b][1]+units[1+b][1] - if len(units)>2: - del units[1] - units.sort(key=lambda u:u[1]) - else: - del units - break - return codes + units = {} # getting element-wise info + for c in data: + if c in units: + units[c] += 1 + else: + units[c] = 1 + units, codes = sorted([([u], units[u]) for u in units], key=lambda u: u[1]), dict.fromkeys(units.keys(), '') + while units: # creating Haffman table + if len(units) > 2: + b = int(units[0][1] + units[1][1] > units[1][1] + units[2][1]) + else: + b = 0 + for c in units[b][0]: + codes[c] = '0' + codes[c] + for c in units[1 + b][0]: + codes[c] = '1' + codes[c] + units[2 * b] = units[b][0] + units[1 + b][0], units[b][1] + units[1 + b][1] + if len(units) > 2: + del units[1] + units.sort(key=lambda u: u[1]) + else: + del units + break + return codes def tbl(table): - table=';'.join([f'{k};{table[k]}' for k in table]).split(';') - byts=[] - for i in range(len(table)): - if i%2: - num=table[i] - else: - num=bin(int(table[i]))[2:] - while len(num)>7: - byts.append(int('1'+num[:7],2)) - num=num[7:] - byts.append(int(num,2)) - byts.append(8-len(num)) - return byts - + table = ';'.join([f'{k};{table[k]}' for k in table]).split(';') + byts = [] + for i in range(len(table)): + if i % 2: + num = table[i] + else: + num = bin(int(table[i]))[2:] + while len(num) > 7: + byts.append(int('1' + num[:7], 2)) + num = num[7:] + byts.append(int(num, 2)) + byts.append(8 - len(num)) + return byts def detbl(byts): - dec=[] - table={} - stack='' - i=0 - while i7: - out.append(int('1'+ln[:7],2)) - ln=ln[7:] - out+=[int(ln,2),8-len(ln)]+table - log.log(f'Huffman table size: {len(out)} bytes.') - log.log('Compressing...') - stack='' - for i in range(len(data)): #encode to Haffman - stack+=hf[data[i]] - while len(stack)>=8: - out.append(int(stack[:8],2)) - stack=stack[8:] - out+=[int(stack.ljust(8,'0'),2),len(stack)] - log.log(f'Compressed size: {len(out)} bytes.') - log.log(f"Saving to '{filename}.hfm'...") - with open(f'{filename}.hfm','wb') as file: #save Haffman code - file.write(bytes(out)) - log.log('SUCCESSFULLY COMPRESSED') - print(f'"origSize":{len(data)},') - print(f'"compSize":{len(out)},') + log.log(f"Loading '{filename}'...") + with open(filename, 'rb') as file: # get data + data = list(map(int, file.read())) + log.log(f'Original size: {len(data)} bytes.') + log.log('Creating Huffman table...') + hf = huffman(data) + table = tbl(hf) + log.log('Embedding Huffman table...') + out = [] + ln = bin(len(table))[2:] # embed the table + while len(ln) > 7: + out.append(int('1' + ln[:7], 2)) + ln = ln[7:] + out += [int(ln, 2), 8 - len(ln)] + table + log.log(f'Huffman table size: {len(out)} bytes.') + log.log('Compressing...') + stack = '' + for i in range(len(data)): # encode to Haffman + stack += hf[data[i]] + while len(stack) >= 8: + out.append(int(stack[:8], 2)) + stack = stack[8:] + out += [int(stack.ljust(8, '0'), 2), len(stack)] + log.log(f'Compressed size: {len(out)} bytes.') + log.log(f"Saving to '{filename}.hfm'...") + with open(f'{filename}.hfm', 'wb') as file: # save Haffman code + file.write(bytes(out)) + log.log('SUCCESSFULLY COMPRESSED') + print(f'"origSize":{len(data)},') + print(f'"compSize":{len(out)},') def decompress_file(filename): - log.log(f"Loading '{filename}'...") - with open(filename,'rb') as file: #get data - data=[bin(byte)[2:].rjust(8,'0')for byte in file.read()] - os=len(data) - data[-2]=data[-2][:int(data[-1],2)] - del data[-1] - log.log('Extracting Huffman table...') - ln='' #extract the table - i=0 - while 1: - if data[i][0]=='1': - ln+=data[i][1:] - else: - ln+=data[i][int(data[i+1],2):] - break - i+=1 - del data[:i+2] - table=detbl(data[:int(ln,2)]) - del data[:int(ln,2)] - data=''.join(data) - stack='' - out=[] - log.log('Decompressing...') - for c in data: #decode Haffman - stack+=c - if stack in table: - out.append(int(table[stack])) - stack='' - filename=filename[:-4] - log.log(f"Saving to '{filename}'...") - with open(f'{filename}','wb') as file: #save decoded data - file.write(bytes(out)) - log.log(f'SUCCESSFULLY DECOMPRESSED') - print(f'"compSize":{os},') - print(f'"origSize":{len(out)},') + log.log(f"Loading '{filename}'...") + with open(filename, 'rb') as file: # get data + data = [bin(byte)[2:].rjust(8, '0') for byte in file.read()] + os = len(data) + data[-2] = data[-2][:int(data[-1], 2)] + del data[-1] + log.log('Extracting Huffman table...') + ln = '' # extract the table + i = 0 + while 1: + if data[i][0] == '1': + ln += data[i][1:] + else: + ln += data[i][int(data[i + 1], 2):] + break + i += 1 + del data[:i + 2] + table = detbl(data[:int(ln, 2)]) + del data[:int(ln, 2)] + data = ''.join(data) + stack = '' + out = [] + log.log('Decompressing...') + for c in data: # decode Haffman + stack += c + if stack in table: + out.append(int(table[stack])) + stack = '' + filename = filename[:-4] + log.log(f"Saving to '{filename}'...") + with open(f'{filename}', 'wb') as file: # save decoded data + file.write(bytes(out)) + log.log(f'SUCCESSFULLY DECOMPRESSED') + print(f'"compSize":{os},') + print(f'"origSize":{len(out)},') @click.command(options_metavar='[-c / -d]') -@click.argument('files',nargs=-1,metavar='') -@click.option('-c/-d','comp',default=True,help='Compress/decompress mode selectors.') -def CLI(files,comp): - log.log(f'hfm {"-c"*comp}{"-d"*(not comp)} {" ".join(files)}') - for file in files: - print('{') - stime=time() - if comp: - compress_file(path(file)) - wtime=time()-stime - print('"status":true,') - print(f'"time":{round(wtime,3)},') - print(f'"dlink":"./files/{basename(file)+".hfm"}"') - else: - try: - decompress_file(path(file)) - wtime=time()-stime - print('"status":true,') - print(f'"time":{round(wtime,3)},') - print(f'"dlink":"./files/{basename(file)[:-4]}"') - except Exception as e: - print(f'"status":false') - print('}') +@click.argument('files', nargs=-1, metavar='') +@click.option('-c/-d', 'comp', default=True, help='Compress/decompress mode selectors.') +def CLI(files, comp): + log.log(f'hfm {"-c" * comp}{"-d" * (not comp)} {" ".join(files)}') + for file in files: + print('{') + stime = time() + if comp: + compress_file(path(file)) + wtime = time() - stime + print('"status":true,') + print(f'"time":{round(wtime, 3)},') + print(f'"dlink":"./files/{basename(file) + ".hfm"}"') + else: + try: + decompress_file(path(file)) + wtime = time() - stime + print('"status":true,') + print(f'"time":{round(wtime, 3)},') + print(f'"dlink":"./files/{basename(file)[:-4]}"') + except Exception as e: + print(f'"status":false') + print('}') -if __name__=='__main__': - CLI() \ No newline at end of file +if __name__ == '__main__': + CLI() diff --git a/huffpress.php b/huffpress.php index fa56c99..1172db7 100644 --- a/huffpress.php +++ b/huffpress.php @@ -1,12 +1,12 @@ \ No newline at end of file diff --git a/bg.webp b/images/bg.webp similarity index 100% rename from bg.webp rename to images/bg.webp diff --git a/processing.gif b/images/processing.gif similarity index 100% rename from processing.gif rename to images/processing.gif diff --git a/index.html b/index.html index cd6e4e7..ddb3b8c 100644 --- a/index.html +++ b/index.html @@ -1,59 +1,76 @@ - - - - HuffPress - - - - - - -
-

HuffPress

-
-
-

Compress your files with the Huffman compression!

-
-
- - + + + + HuffPress + + + + + + +
+

HuffPress

+
+
+

Compress your files with the Huffman compression!

+ +
+ + +
+
+
+ + +
Let's choose your file!
+
Ready to start!
+
+
+
+ +
+ +
+
+
+ Processing...
-
-
- - -
Let's choose your file!
-
Ready to start!
+
+

Completed!

+

+
+ Download +
-
- -
- -
-
-
Processing...
-
-

Completed!

-

-
- Download - +
+

Error!

+

Unable to decompress this file!

+
+ +
-
-

Error!

-

Unable to decompress this file!

-
- -
-
-
- - - \ No newline at end of file + + + diff --git a/script.js b/script.js index cfa541b..93462b4 100644 --- a/script.js +++ b/script.js @@ -1,76 +1,88 @@ -function winsize(){ - if (!toggled && $(window).width()/$(window).height()>(90/31) || - toggled && $(window).width()/($(window).height()-219)>(18/7)){ - $('body').css('justify-content','flex-start'); +function winsize() { + if ( + (!toggled && $(window).width() / $(window).height() > 90 / 31) || + (toggled && $(window).width() / ($(window).height() - 219) > 18 / 7) + ) { + $('body').css('justify-content', 'flex-start') } else { - $('body').css('justify-content','center'); + $('body').css('justify-content', 'center') } -}; -window.onload=function(){ - setTimeout(()=>{$('main').slideDown(500)},100); - winsize(); -}; -$(window).on('resize',function(){ - winsize(); -}); +} +window.onload = function () { + setTimeout(() => { + $('main').slideDown(500) + }, 100) + winsize() +} +$(window).on('resize', function () { + winsize() +}) -var toggled=false; -$('h2').click(function(){ - var time=300; +var toggled = false +$('h2').click(function () { + var time = 300 if (toggled) { - $('form').slideUp(time); - $('main').css('min-height','9vw'); - setTimeout(()=>{$('h2').css('border-bottom','0')},time); - toggled=false; - winsize(); + $('form').slideUp(time) + $('main').css('min-height', '9vw') + setTimeout(() => { + $('h2').css('border-bottom', '0') + }, time) + toggled = false + winsize() } else { - $('main').css('min-height','calc(9vw + 260px)'); - $('form').slideDown(time); - $('form').css('display','flex'); - $('h2').css('border-bottom','1px solid black'); - toggled=true; - setTimeout(()=>{winsize()},time); + $('main').css('min-height', 'calc(9vw + 260px)') + $('form').slideDown(time) + $('form').css('display', 'flex') + $('h2').css('border-bottom', '1px solid black') + toggled = true + setTimeout(() => { + winsize() + }, time) } -}); +}) -$('form').on('submit',function submit(e){ - e.preventDefault(); - $('.wrap').css('display','flex'); - $('.process').css('display','block'); - var form=new FormData(); - form.append('mode',$('#mode').val()); - $.each($('#file')[0].files, function(i, file) { - form.append('file', file); - }); +$('form').on('submit', function submit(e) { + e.preventDefault() + $('.wrap').css('display', 'flex') + $('.process').css('display', 'block') + var form = new FormData() + form.append('mode', $('#mode').val()) + $.each($('#file')[0].files, function (i, file) { + form.append('file', file) + }) $.ajax({ - url: 'huffpress.php', - type: 'POST', + url: 'huffpress.php', + type: 'POST', processData: false, contentType: false, - dataType: 'json', - data: form, - success: function(resp){ - console.log(resp); - if (resp.status){ - $('.process').css('display','none'); - $('.complete').css('display','block'); - if ($('#mode').val()=='compress'){ - $('.complete .status').html(`Original size:   ${resp.origSize} B
Compressed size: ${resp.compSize} B
Time:            ${resp.time} s`); + dataType: 'json', + data: form, + success: function (resp) { + console.log(resp) + if (resp.status) { + $('.process').css('display', 'none') + $('.complete').css('display', 'block') + if ($('#mode').val() == 'compress') { + $('.complete .status').html( + `Original size:   ${resp.origSize} B
Compressed size: ${resp.compSize} B
Time:            ${resp.time} s` + ) } else { - $('.complete .status').html(`Compressed size: ${resp.compSize} B
Original size:   ${resp.origSize} B
Time:            ${resp.time} s`); + $('.complete .status').html( + `Compressed size: ${resp.compSize} B
Original size:   ${resp.origSize} B
Time:            ${resp.time} s` + ) } - $('#dlink').attr('href',resp.dlink); + $('#dlink').attr('href', resp.dlink) } else { - $('.process').css('display','none'); - $('.error').css('display','block'); + $('.process').css('display', 'none') + $('.error').css('display', 'block') } - } - }); -}); + }, + }) +}) -$('.closebtn').click(function(){ - $('.wrap').css('display','none'); - $('.process').css('display','none'); - $('.error').css('display','none'); - $('.complete').css('display','none'); -}); \ No newline at end of file +$('.closebtn').click(function () { + $('.wrap').css('display', 'none') + $('.process').css('display', 'none') + $('.error').css('display', 'none') + $('.complete').css('display', 'none') +}) diff --git a/style.css b/style.css index 96fdef3..8b3a60d 100644 --- a/style.css +++ b/style.css @@ -1,11 +1,12 @@ @import url('https://fonts.googleapis.com/css2?family=Epilogue&family=Secular+One&display=swap'); -html,body{ +html, +body { width: 100%; min-height: 100vh; margin: 0; padding: 0; } -body{ +body { position: absolute; left: 0; right: 0; @@ -15,16 +16,16 @@ body{ flex-direction: column; justify-content: center; align-items: center; - background-image: url(bg.webp); + background-image: url(./images/bg.webp); background-size: cover; } -header{ +header { margin: 0; margin-top: 2vw; padding: 0; text-align: center; } -h1{ +h1 { margin: 0; padding: 0; color: white; @@ -33,21 +34,21 @@ h1{ text-shadow: 0 0 1vw black; cursor: default; } -main{ +main { display: none; margin-bottom: 2vw; background-color: #fff8; - box-shadow: 0 0 .5vw black; + box-shadow: 0 0 0.5vw black; border-radius: 1.5vw; min-height: 9vw; - transition: .3s; + transition: 0.3s; overflow: hidden; } -main:hover{ +main:hover { background-color: #fff; box-shadow: 0 0 1vw black; } -h2{ +h2 { margin: 0; padding: 0 2vw; height: 9vw; @@ -55,14 +56,14 @@ h2{ flex-direction: column; justify-content: center; align-items: center; - background-color: hsla(270,80%,80%,.6); + background-color: hsla(270, 80%, 80%, 0.6); border-bottom: 0; font-family: Secular One; font-size: 3vw; - text-shadow: 2.5px 2px .5px white; + text-shadow: 2.5px 2px 0.5px white; cursor: pointer; } -form{ +form { display: flex; flex-direction: column; justify-content: center; @@ -71,7 +72,7 @@ form{ padding: 2vw 2vw; box-sizing: border-box; } -.wrap{ +.wrap { display: none; position: absolute; left: 0; @@ -83,7 +84,9 @@ form{ align-items: center; z-index: 10; } -.process,.complete,.error{ +.process, +.complete, +.error { display: none; padding: 2vw 5vw; background-color: white; @@ -92,28 +95,28 @@ form{ font-size: 5vw; cursor: default; } -.process img{ +.process img { width: 15vw; } -h3{ +h3 { margin: 0; - margin-top: .5vw; + margin-top: 0.5vw; font-family: Secular One; font-size: 3vw; } -.status{ +.status { margin: 0; - margin-top: .5vw; + margin-top: 0.5vw; font-family: monospace; font-size: 1.5vw; } -.btncont{ +.btncont { margin: 0; padding: 0; } -.btncont *{ +.btncont * { font-size: 2vw; } -#dlink{ +#dlink { margin-right: 1vw; -} \ No newline at end of file +}