mirror of
https://github.com/Akascape/Datamosher-Pro.git
synced 2025-12-11 02:39:58 +01:00
Compare commits
97 Commits
Datamosher
...
Datamosher
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92446525e9 | ||
|
|
23898d12a0 | ||
|
|
aa02f643fb | ||
|
|
7b7eb84333 | ||
|
|
09f787a635 | ||
|
|
56a498ab15 | ||
|
|
d94da16c30 | ||
|
|
1568a58195 | ||
|
|
af607eb157 | ||
|
|
36af34b55d | ||
|
|
a9dd3dd677 | ||
|
|
1fc8caaec9 | ||
|
|
54f79a2ed6 | ||
|
|
81144da0f9 | ||
|
|
51c96258e9 | ||
|
|
eee6b5d4a4 | ||
|
|
520dbe8349 | ||
|
|
88f9923a0a | ||
|
|
ce3aceb3ed | ||
|
|
3ce55cecb7 | ||
|
|
11b3248924 | ||
|
|
57c25c0026 | ||
|
|
b03823dbcd | ||
|
|
9d071b723a | ||
|
|
2e27e84732 | ||
|
|
f79e8fc735 | ||
|
|
d179113543 | ||
|
|
d5ad710835 | ||
|
|
943d64fb42 | ||
|
|
de3a1fa7e1 | ||
|
|
2c65f8d682 | ||
|
|
57946f1909 | ||
|
|
87456ecd7f | ||
|
|
13d333b412 | ||
|
|
4e433eab4e | ||
|
|
b561d9354a | ||
|
|
f69ad552a7 | ||
|
|
c129e8f558 | ||
|
|
89904a2578 | ||
|
|
0a8814343a | ||
|
|
7e6a480e89 | ||
|
|
2084d8c349 | ||
|
|
670d39e8b2 | ||
|
|
953cb59eff | ||
|
|
f9aa09153b | ||
|
|
dcaae608a5 | ||
|
|
f8ec406722 | ||
|
|
687fe27530 | ||
|
|
7e39a87d23 | ||
|
|
08a4ce9f50 | ||
|
|
5fb453d295 | ||
|
|
0602a2d618 | ||
|
|
f2f78dd5a6 | ||
|
|
625fc0f4fd | ||
|
|
8f35e9af4b | ||
|
|
424e857424 | ||
|
|
ec7e6843a6 | ||
|
|
26f9ec3ace | ||
|
|
b67371f580 | ||
|
|
4c89bd3264 | ||
|
|
334e0afd2e | ||
|
|
af3efe0dc9 | ||
|
|
983c0fdbc2 | ||
|
|
55303c1dcc | ||
|
|
e67d281e4b | ||
|
|
a9b53b07c0 | ||
|
|
7c6482e060 | ||
|
|
fc56de9bcc | ||
|
|
c8ffd74a45 | ||
|
|
afec2dd728 | ||
|
|
97c2c5c55b | ||
|
|
a9b3d8e444 | ||
|
|
583d4f2e4d | ||
|
|
5be68f3a7f | ||
|
|
5508f57eba | ||
|
|
9535b09776 | ||
|
|
efa3ddbde6 | ||
|
|
485b404cc7 | ||
|
|
c4c4e63185 | ||
|
|
8d5ca45528 | ||
|
|
a040b89bb0 | ||
|
|
544ccc2979 | ||
|
|
2714de88f9 | ||
|
|
c1fba754a5 | ||
|
|
047042cfbb | ||
|
|
af6f20df69 | ||
|
|
517a402c10 | ||
|
|
1f2e620540 | ||
|
|
6102604a1e | ||
|
|
f66a780dda | ||
|
|
fe08676f8c | ||
|
|
9cd1a6fba2 | ||
|
|
d12acc102f | ||
|
|
ecc221c7f1 | ||
|
|
6d000ec3ee | ||
|
|
e1ac12ab81 | ||
|
|
e2296abafe |
@@ -3,23 +3,52 @@ from tkinter import *
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
import tkinter
|
||||
import sys
|
||||
import imageio
|
||||
import random
|
||||
import re
|
||||
import struct
|
||||
import time
|
||||
import webbrowser
|
||||
from itertools import chain, repeat
|
||||
from itertools import chain, repeat, islice
|
||||
import subprocess
|
||||
import pkg_resources
|
||||
#Note that this program is optimised for only windows, for other systems you have to change the ffmpeg path(line 48).
|
||||
required = {'imageio', 'imageio-ffmpeg'}
|
||||
installed = {pkg.key for pkg in pkg_resources.working_set}
|
||||
missing = required - installed
|
||||
missingset=[*missing,]
|
||||
if missing:
|
||||
res=messagebox.askquestion("Module Error","Some modules are not installed \n do you want to download and install them?")
|
||||
if res=="yes":
|
||||
for x in range(len(missingset)):
|
||||
y=missingset[x]
|
||||
subprocess.Popen('python -m pip install '+y)
|
||||
messagebox.showinfo("Module Installed","Please restart the program!")
|
||||
sys.exit()
|
||||
elif res=="no":
|
||||
messagebox.showerror("Error","Required modules not available!\nWithout the modules you can't use this program. Please install them first!")
|
||||
sys.exit()
|
||||
else:
|
||||
import imageio
|
||||
if os.path.isdir("pymosh"):
|
||||
from pymosh import Index
|
||||
from pymosh.codec.mpeg4 import is_iframe
|
||||
else:
|
||||
messagebox.showerror("Missing Folder!","Pymosh folder is not available! Please download it from our github page.")
|
||||
sys.exit()
|
||||
def resource_path0(relative_path):
|
||||
base_path = getattr(
|
||||
sys,
|
||||
'_MEIPASS',
|
||||
os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.join(base_path, relative_path)
|
||||
#ffmpeg path:
|
||||
python_folder=os.path.dirname(sys.executable)
|
||||
path=python_folder.replace(os.sep, '/')
|
||||
global resource
|
||||
resource=resource_path0(path+"/Lib/site-packages/imageio_ffmpeg/binaries/ffmpeg-win64-v4.2.2.exe")
|
||||
def openfile():
|
||||
global file
|
||||
file=tkinter.filedialog.askopenfilename(filetypes =[('MP4', '*.mp4'),('AVI', '*.avi'),('GIF','*.gif'),('MOV','*.mov')])
|
||||
file=tkinter.filedialog.askopenfilename(filetypes =[('Video', ['*.mp4','*.avi','*.mov','*.mkv','*wmv']),('All Files', '*.*')])
|
||||
if(len(file)>1):
|
||||
LocationError.config(text=file, fg="green")
|
||||
OpeningFile['text']='Open Again'
|
||||
@@ -33,8 +62,6 @@ def convertffmpeg(inputpath):
|
||||
root.update()
|
||||
fps=30
|
||||
targetformat='.avi'
|
||||
#Change the imageio-ffmpeg path if not working (also in the datamoshclassic function)
|
||||
resource=resource_path0("C:/Users/User/AppData/Local/Programs/Python/Python39/Lib/site-packages/imageio_ffmpeg/binaries/ffmpeg-win64-v4.2.2.exe")
|
||||
outputpath=os.path.splitext(inputpath)[0]+'_datamoshed'+targetformat
|
||||
subprocess.call(f'"{resource}" -loglevel error -y -i "{inputpath}" -crf 0 -bf 0 -r {fps} "{outputpath}"', shell=True)
|
||||
try:
|
||||
@@ -46,7 +73,10 @@ def convertffmpeg(inputpath):
|
||||
Wait.place_forget()
|
||||
os.remove(outputpath)
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
root.config(cursor="")
|
||||
def convert(inputpath,targetformat):
|
||||
global outputpath
|
||||
try:
|
||||
@@ -58,7 +88,7 @@ def convert(inputpath,targetformat):
|
||||
writer= imageio.get_writer(outputpath, fps=fps)
|
||||
for frames in reader:
|
||||
writer.append_data(frames)
|
||||
#for detailed logs of conversions, remove the comment in the next 2 lines
|
||||
#to get detailed logs of conversions, remove the comment in the next 2 lines
|
||||
#print(f'Frame {frames}')
|
||||
#print("Converted")
|
||||
writer.close()
|
||||
@@ -66,21 +96,53 @@ def convert(inputpath,targetformat):
|
||||
Datamosh(outputpath)
|
||||
except:
|
||||
pass
|
||||
except Exception:
|
||||
except:
|
||||
messagebox.showerror("FAILED","The video file or the input data have \n some issues!")
|
||||
Wait.place_forget()
|
||||
os.remove(outputpath)
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
root.config(cursor="")
|
||||
def validate():
|
||||
try:
|
||||
float(Countframe.get())
|
||||
float(Positframe.get())
|
||||
float(firstframes.get())
|
||||
float(killframe.get())
|
||||
x=float(killframe.get())
|
||||
if x>1:
|
||||
var4.set(1)
|
||||
except ValueError:
|
||||
messagebox.showerror("Invalid Input","Please enter some valid data")
|
||||
sys.exit()
|
||||
def checkexist(file):
|
||||
mode = modechoices.get()
|
||||
tformat=exportbox.get()
|
||||
f=os.path.splitext(file)[0]
|
||||
exfile=f+"_datamoshed"+"-"+mode+"_datamoshed."+tformat
|
||||
if os.path.exists(exfile):
|
||||
warn=messagebox.askquestion("Warning","Do you want to replace the old file?")
|
||||
if warn=='yes':
|
||||
os.remove(exfile)
|
||||
elif warn=='no':
|
||||
os.kill(checkesist())
|
||||
pass
|
||||
def Step1():
|
||||
try:
|
||||
if (len(file)>=1):
|
||||
Wait.place(relx=0.5,rely=0.85,anchor='center')
|
||||
root.update()
|
||||
extension=os.path.splitext(file)[1]
|
||||
datamoshbtn['state']=DISABLED
|
||||
datamoshbtn['cursor']='watch'
|
||||
choice = modechoices.get()
|
||||
extension=os.path.splitext(file)[1]
|
||||
modechoices['state']=DISABLED
|
||||
exportbox['state']=DISABLED
|
||||
OpeningFile['state']=DISABLED
|
||||
validate()
|
||||
checkexist(file)
|
||||
root.config(cursor="")
|
||||
choice = modechoices.get()
|
||||
if(choice==modes[7]):
|
||||
if extension==".mp4":
|
||||
convertffmpeg(file)
|
||||
@@ -88,18 +150,120 @@ def Step1():
|
||||
targetformat=".mp4"
|
||||
convert(file,targetformat)
|
||||
convertffmpeg(file)
|
||||
else:
|
||||
if extension==".avi":
|
||||
Datamosh(file)
|
||||
else:
|
||||
targetformat=".avi"
|
||||
convert(file,targetformat)
|
||||
elif(choice==modes[8]) or (choice==modes[9]) or (choice==modes[11]):
|
||||
pymosh_library(file)
|
||||
else:
|
||||
targetformat=".avi"
|
||||
convert(file,targetformat)
|
||||
else:
|
||||
messagebox.showerror("OOPS","Please choose the video again!")
|
||||
messagebox.showerror("","Please choose the video again!")
|
||||
except:
|
||||
messagebox.showerror("OOPS","Please choose the video!")
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
root.config(cursor="")
|
||||
Wait.place_forget()
|
||||
messagebox.showerror("","Please choose the video file again!")
|
||||
def pymosh_library(file):
|
||||
global final
|
||||
outpath=os.path.dirname(file)
|
||||
outx=os.path.basename(file).split('.')[0]
|
||||
infile=os.path.dirname(file)+"/"+outx+"_datamoshed.avi"
|
||||
if os.path.exists(infile):
|
||||
os.remove(infile)
|
||||
fps=30
|
||||
subprocess.call(f'"{resource}" -loglevel error -y -i "{file}" -crf 0 -bf 0 -r {fps} "{infile}"', shell=True)
|
||||
def glide(interval, filename, outfile):
|
||||
f = Index.from_file(filename)
|
||||
buf = [None]
|
||||
def process_frame(frame):
|
||||
if buf[0] == None or not is_iframe(frame):
|
||||
buf[0] = frame
|
||||
else:
|
||||
frame = buf[0]
|
||||
return frame
|
||||
for stream in f.video:
|
||||
newstream = []
|
||||
newstream.append(stream[0])
|
||||
ix = 0
|
||||
jx = 0
|
||||
for i in stream[1:]:
|
||||
ix += 1
|
||||
jx += 1
|
||||
if ix < interval:
|
||||
newstream.append(process_frame(stream[jx]))
|
||||
else:
|
||||
newstream.append(newstream[-1])
|
||||
if ix > interval * 2:
|
||||
ix = 0
|
||||
stream.replace(newstream)
|
||||
f.rebuild()
|
||||
with open(outfile, 'wb') as out:
|
||||
f.write(out)
|
||||
def avi_sort(filename, outfile):
|
||||
f = Index.from_file(filename)
|
||||
for stream in f.video:
|
||||
sorted_stream = sorted(stream, key=len, reverse=True)
|
||||
stream.replace(sorted_stream)
|
||||
f.rebuild()
|
||||
with open(outfile, 'wb') as out:
|
||||
f.write(out)
|
||||
def process_streams(in_filename, out_filename, func, *args, **kwargs):
|
||||
f = Index.from_file(in_filename)
|
||||
for stream in f.video:
|
||||
midpoint=float(firstframes.get())
|
||||
if midpoint>1:
|
||||
midpoint=1
|
||||
drifted = list(func(stream, midpoint,*args, **kwargs))
|
||||
stream.replace(drifted)
|
||||
f.rebuild()
|
||||
with open(out_filename, 'wb') as out:
|
||||
f.write(out)
|
||||
def echo(stream, midpoint):
|
||||
all_frames = list(stream)
|
||||
pframes = [f for f in all_frames if not is_iframe(f)]
|
||||
midpoint_idx = int(len(all_frames)*midpoint)
|
||||
frames = all_frames[:midpoint_idx]
|
||||
while len(frames) < len(all_frames):
|
||||
frames += pframes[:(len(all_frames) - len(frames))]
|
||||
return frames
|
||||
choice = modechoices.get()
|
||||
if(choice==modes[8]):
|
||||
Wait.config(text="Applying Effect: Glide", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
time.sleep(2)
|
||||
interval=int(Positframe.get())
|
||||
if (interval==1):
|
||||
interval=2
|
||||
outfile=outpath+"/"+os.path.basename(infile).split('.')[0]+"-glide.avi"
|
||||
if os.path.exists(outfile):
|
||||
os.remove(outfile)
|
||||
glide(interval, infile, outfile)
|
||||
elif (choice==modes[9]):
|
||||
Wait.config(text="Applying Effect: Sort", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
time.sleep(2)
|
||||
outfile=outpath+"/"+os.path.basename(infile).split('.')[0]+"-sort.avi"
|
||||
if os.path.exists(outfile):
|
||||
os.remove(outfile)
|
||||
avi_sort(infile, outfile)
|
||||
elif (choice==modes[11]):
|
||||
Wait.config(text="Applying Effect: Echo", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
time.sleep(2)
|
||||
outfile=outpath+"/"+os.path.basename(infile).split('.')[0]+"-echo.avi"
|
||||
if os.path.exists(outfile):
|
||||
os.remove(outfile)
|
||||
process_streams(infile, outfile, echo)
|
||||
final=outpath+"/"+os.path.basename(outfile).split('.')[0]+".mp4"
|
||||
if os.path.exists(final):
|
||||
os.remove(final)
|
||||
subprocess.call(f'"{resource}" -loglevel error -y -i "{outfile}" "{final}"', shell=True)
|
||||
export2=exportbox.get()
|
||||
os.remove(infile)
|
||||
export(final)
|
||||
os.remove(outfile)
|
||||
def Datamoshclassic(filename,mainfile):
|
||||
global outf
|
||||
END_FRAME_HEX = b'00dc'
|
||||
@@ -107,16 +271,18 @@ def Datamoshclassic(filename,mainfile):
|
||||
fps=30
|
||||
outx=os.path.basename(filename).split('.')[0]
|
||||
outpath=os.path.dirname(mainfile)
|
||||
outf=outpath+"/"+outx+"_classic_datamoshed.avi"
|
||||
def main2(filename, effect_sec_list, p_frames_mult=1):
|
||||
outf=outpath+"/"+outx+"-classic_datamoshed.avi"
|
||||
def main2(filename, effect_sec_list, p_frames_mult):
|
||||
magic(effect_sec_list, p_frames_mult)
|
||||
out=outpath+"/"+outx+"_classic_datamoshed.mp4"
|
||||
#Change the imageio-ffmpeg path here also if not working
|
||||
resource=resource_path0("C:/Users/User/AppData/Local/Programs/Python/Python39/Lib/site-packages/imageio_ffmpeg/binaries/ffmpeg-win64-v4.2.2.exe")
|
||||
out=outpath+"/"+outx+"-classic.mp4"
|
||||
subprocess.call(f'"{resource}" -loglevel error -y -i "{outf}" "{out}"', shell=True)
|
||||
os.remove(filename)
|
||||
export(out)
|
||||
os.remove(outf)
|
||||
export2=exportbox.get()
|
||||
if(export2==exportchoices[0]):
|
||||
pass
|
||||
else:
|
||||
os.remove(outf)
|
||||
def magic(effect_sec_list, p_frames_mult):
|
||||
with open(filename, 'rb') as in_file, open(outf, 'wb') as out_file:
|
||||
frames = split_file(in_file, END_FRAME_HEX)
|
||||
@@ -141,12 +307,14 @@ def Datamoshclassic(filename,mainfile):
|
||||
return any(start < curr_sec < end for start, end in effect_sec_list)
|
||||
def is_iframe(frame):
|
||||
return frame[5:8] == I_FRAME_HEX
|
||||
start=int(firstframes.get())
|
||||
make=int(Countframe.get())
|
||||
start=float(firstframes.get())
|
||||
make=float(Countframe.get())
|
||||
timer=int(Positframe.get())
|
||||
main2(filename,[(start,make)],timer)
|
||||
def Datamosh(Inputfile):
|
||||
if os.path.splitext(outputpath)[1]==".avi":
|
||||
global fileout
|
||||
checkinput=os.path.splitext(file)[0]+"_datamoshed.avi"
|
||||
if os.path.exists(checkinput):
|
||||
filein = Inputfile
|
||||
countframes = int(Countframe.get())
|
||||
positframes = int(Positframe.get())
|
||||
@@ -167,28 +335,26 @@ def Datamosh(Inputfile):
|
||||
mode="reverse"
|
||||
elif(choice==modes[6]):
|
||||
mode="random"
|
||||
elif(choice==modes[10]):
|
||||
mode="shake"
|
||||
elif(choice==modes[12]):
|
||||
mode="void"
|
||||
else:
|
||||
messagebox.showerror("OOPS","Please Select the Correct Mode!")
|
||||
messagebox.showerror("Error!","Please select a valid mode!")
|
||||
os.remove(outputpath)
|
||||
os.kill(Datamosh())
|
||||
Wait.place_forget()
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
if filein is None or os.path.exists(filein) == False:
|
||||
messagebox.showerror("ERROR","Input File is Missing!")
|
||||
Wait.place_forget()
|
||||
os.remove(outputpath)
|
||||
datamoshbtn['state']=NORMAL
|
||||
os.kill(Datamosh())
|
||||
datamoshbtn['cursor']=''
|
||||
#define temp directory and files
|
||||
root.config(cursor="")
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
os.kill(Datamosh())
|
||||
temp_nb = random.randint(10000, 99999)
|
||||
temp_dir = "temp-" + str(temp_nb)
|
||||
temp_hdrl = temp_dir +"/hdrl.bin"
|
||||
temp_movi = temp_dir +"/movi.bin"
|
||||
temp_idx1 = temp_dir +"/idx1.bin"
|
||||
os.mkdir(temp_dir)
|
||||
#Define constrain function for jiggle :3
|
||||
def constrain(val, min_val, max_val):
|
||||
return min(max_val, max(min_val, val))
|
||||
def bstream_until_marker(bfilein, bfileout, marker=0, startpos=0):
|
||||
@@ -205,8 +371,8 @@ def Datamosh(Inputfile):
|
||||
|
||||
if marker:
|
||||
if buffer.find(marker) > 0 :
|
||||
marker_pos = re.search(marker, buffer).start() # position is relative to buffer glitchedframes
|
||||
marker_pos = marker_pos + pos # position should be absolute now
|
||||
marker_pos = re.search(marker, buffer).start()
|
||||
marker_pos = marker_pos + pos
|
||||
split = buffer.split(marker, 1)
|
||||
wr.write(split[0])
|
||||
return marker_pos
|
||||
@@ -214,7 +380,6 @@ def Datamosh(Inputfile):
|
||||
wr.write(buffer)
|
||||
else:
|
||||
wr.write(buffer)
|
||||
#make 3 files, 1 for each chunk
|
||||
movi_marker_pos = bstream_until_marker(filein, temp_hdrl, "movi")
|
||||
idx1_marker_pos = bstream_until_marker(filein, temp_movi, "idx1", movi_marker_pos)
|
||||
bstream_until_marker(filein, temp_idx1, 0, idx1_marker_pos)
|
||||
@@ -226,17 +391,14 @@ def Datamosh(Inputfile):
|
||||
for pos in range(0, filesize, chunk):
|
||||
rd.seek(pos)
|
||||
buffer = rd.read(chunk)
|
||||
#build first list with all adresses
|
||||
for m in (re.finditer(b'\x30\x31\x77\x62', buffer)): # find iframes
|
||||
for m in (re.finditer(b'\x30\x31\x77\x62', buffer)):
|
||||
if audio : frame_table.append([m.start() + pos, 'sound'])
|
||||
for m in (re.finditer(b'\x30\x30\x64\x63', buffer)): # find b frames
|
||||
for m in (re.finditer(b'\x30\x30\x64\x63', buffer)):
|
||||
frame_table.append([m.start() + pos, 'video'])
|
||||
#then remember to sort the list
|
||||
frame_table.sort(key=lambda tup: tup[0])
|
||||
l = []
|
||||
l.append([0,0, 'void'])
|
||||
max_frame_size = 0
|
||||
#build tuples for each frame index with frame sizes
|
||||
for n in range(len(frame_table)):
|
||||
if n + 1 < len(frame_table):
|
||||
frame_size = frame_table[n + 1][0] - frame_table[n][0]
|
||||
@@ -244,20 +406,18 @@ def Datamosh(Inputfile):
|
||||
frame_size = filesize - frame_table[n][0]
|
||||
max_frame_size = max(max_frame_size, frame_size)
|
||||
l.append([frame_table[n][0],frame_size, frame_table[n][1]])
|
||||
# variables that make shit work
|
||||
clean = []
|
||||
final = []
|
||||
# keep first video frame or not
|
||||
if firstframe :
|
||||
for x in l :
|
||||
if x[2] == 'video':
|
||||
clean.append(x)
|
||||
break
|
||||
# clean the list by killing "big" frames
|
||||
for x in l:
|
||||
if x[1] <= (max_frame_size * kill) :
|
||||
clean.append(x)
|
||||
# FX modes
|
||||
if mode == "void":
|
||||
final = clean
|
||||
if mode == "random":
|
||||
Wait.config(text="Applying Effect: Random", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
@@ -265,7 +425,7 @@ def Datamosh(Inputfile):
|
||||
if mode == "reverse":
|
||||
Wait.config(text="Applying Effect: Reverse", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
final = clean[::-1]
|
||||
final = sum(zip(clean[::-1], clean[:-1]), ())
|
||||
if mode == "invert":
|
||||
Wait.config(text="Applying Effect: Invert", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
@@ -275,10 +435,8 @@ def Datamosh(Inputfile):
|
||||
root.update()
|
||||
repeat = int(countframes)
|
||||
frame = int(positframes)
|
||||
## split list
|
||||
lista = clean[:frame]
|
||||
listb = clean[frame:]
|
||||
## rejoin list with bloom
|
||||
final = lista + ([clean[frame]]*repeat) + listb
|
||||
if mode == 'pulse':
|
||||
Wait.config(text="Applying Effect: Pulse", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
@@ -306,13 +464,44 @@ def Datamosh(Inputfile):
|
||||
pulselen = int(countframes)
|
||||
pulseryt = int(positframes)
|
||||
clean = [clean[i:i+pulselen] for i in range(0,len(clean),pulseryt)]
|
||||
final = [item for sublist in clean for item in sublist]
|
||||
final = [item for sublist in clean for item in sublist]
|
||||
if mode == "shake":
|
||||
Wait.config(text="Applying Effect: Shake", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
def process_streams(in_filename, out_filename, func, *args, **kwargs):
|
||||
f = Index.from_file(in_filename)
|
||||
for stream in f.video:
|
||||
drifted = list(func(stream, *args, **kwargs))
|
||||
stream.replace(drifted)
|
||||
f.rebuild()
|
||||
with open(out_filename, 'wb') as out:
|
||||
f.write(out)
|
||||
def shake(stream):
|
||||
def glitch(stream):
|
||||
all_frames = iter(stream)
|
||||
yield next(all_frames)
|
||||
while True:
|
||||
frame = next(all_frames)
|
||||
if not is_iframe(frame):
|
||||
yield frame
|
||||
yield frame
|
||||
return islice(glitch(stream), len(stream))
|
||||
fileout= filein[:-4] + '-' + mode + '.avi'
|
||||
process_streams(filein, fileout, shake)
|
||||
fps=30
|
||||
output= fileout[:-9]+"shake.mp4"
|
||||
subprocess.call(f'"{resource}" -loglevel error -y -i "{fileout}" -crf 0 -bf 0 -r {fps} "{output}"', shell=True)
|
||||
time.sleep(2)
|
||||
os.remove(fileout)
|
||||
os.remove(filein)
|
||||
os.remove(temp_hdrl)
|
||||
os.remove(temp_movi)
|
||||
os.remove(temp_idx1)
|
||||
os.rmdir(temp_dir)
|
||||
export(output)
|
||||
os.kill(datamosh)
|
||||
time.sleep(2)
|
||||
#name new file
|
||||
cname = '-c' + str(countframes) if int(countframes) > 1 else ''
|
||||
pname = '-n' + str(positframes) if int(positframes) > 1 else ''
|
||||
fileout= filein[:-4] + '-' + mode + cname + pname + '.avi'
|
||||
#delete old file
|
||||
fileout= filein[:-4] + '-' + mode + '.avi'
|
||||
if os.path.exists(fileout):
|
||||
os.remove(fileout)
|
||||
bstream_until_marker(temp_hdrl, fileout)
|
||||
@@ -341,40 +530,63 @@ def export(fileout):
|
||||
def removefileout():
|
||||
if os.path.exists(fileout):
|
||||
os.remove(fileout)
|
||||
messagebox.showinfo("DONE","Datamoshed video is ready!")
|
||||
Wait.place_forget()
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
root.config(cursor="")
|
||||
messagebox.showinfo("DONE","Datamoshed video is ready!")
|
||||
Wait.config(text="", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
else:
|
||||
pass
|
||||
export=exportbox.get()
|
||||
if(export==exportchoices[0]):
|
||||
ask=messagebox.askquestion("?","Do you want the Raw moshed version?")
|
||||
if ask=='yes':
|
||||
choice = modechoices.get()
|
||||
if(choice==modes[7]):
|
||||
Wait.config(text="Moshed Video is Ready!", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
os.remove(fileout)
|
||||
Wait.config(text="Moshed Video is Ready!", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
messagebox.showinfo("DONE","Datamoshed video is ready!")
|
||||
Wait.place_forget()
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
os.kill(Datamoshclassic())
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
root.config(cursor="")
|
||||
else:
|
||||
file2=os.path.splitext(fileout)[0]
|
||||
os.rename(fileout,file2+"_datamoshed.avi")
|
||||
Wait.config(text="Moshed Video is Ready!", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
messagebox.showinfo("DONE","Datamoshed video is ready!")
|
||||
root.config(cursor="")
|
||||
Wait.place_forget()
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
elif ask=='no':
|
||||
targetformat=".avi"
|
||||
convert(fileout,targetformat)
|
||||
removefileout()
|
||||
elif(export==exportchoices[1]):
|
||||
choice = modechoices.get()
|
||||
if(choice==modes[7]):
|
||||
if(choice==modes[7]) or (choice==modes[8]) or (choice==modes[9]):
|
||||
nfile=os.path.splitext(fileout)[0]
|
||||
os.rename(fileout,nfile+"_datamoshed.mp4")
|
||||
Wait.config(text="Moshed Video is Ready!", fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
root.update()
|
||||
messagebox.showinfo("DONE","Datamoshed video is ready!")
|
||||
Wait.place_forget()
|
||||
root.config(cursor="")
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
else:
|
||||
targetformat=".mp4"
|
||||
convert(fileout,targetformat)
|
||||
@@ -396,21 +608,36 @@ def export(fileout):
|
||||
convert(fileout,targetformat)
|
||||
removefileout()
|
||||
else:
|
||||
messagebox.showerror("OOPS","Please Select the Correct Format!")
|
||||
os.remove(outputpath)
|
||||
os.kill(Datamosh())
|
||||
messagebox.showerror("Error!","Please select a valid video format!")
|
||||
os.remove(fileout)
|
||||
Wait.place_forget()
|
||||
datamoshbtn['state']=NORMAL
|
||||
datamoshbtn['cursor']=''
|
||||
modechoices['state']=NORMAL
|
||||
exportbox['state']=NORMAL
|
||||
OpeningFile['state']=NORMAL
|
||||
root.config(cursor="")
|
||||
os.kill(Datamosh())
|
||||
def toggle():
|
||||
if var.get() == 0:
|
||||
if var.get()==0:
|
||||
Disableadvanced()
|
||||
else:
|
||||
Enableadvanced()
|
||||
def Enableadvanced():
|
||||
choice = modechoices.get()
|
||||
if(choice==modes[8]):
|
||||
var2.set(6)
|
||||
var4.set(0.7)
|
||||
countlabel['fg']="grey"
|
||||
positlabel['fg']="green"
|
||||
firstflabel['fg']="grey"
|
||||
killlabel['fg']="grey"
|
||||
Countframe['state']=DISABLED
|
||||
Positframe['state']=NORMAL
|
||||
firstframes['state']=DISABLED
|
||||
killframe['state']=DISABLED
|
||||
if(choice==modes[7]):
|
||||
var1.set("3")
|
||||
var2.set(1)
|
||||
var1.set(100)
|
||||
countlabel['fg']="green"
|
||||
positlabel['fg']="green"
|
||||
firstflabel['fg']="green"
|
||||
@@ -419,7 +646,13 @@ def Enableadvanced():
|
||||
Positframe['state']=NORMAL
|
||||
firstframes['state']=NORMAL
|
||||
killframe['state']=DISABLED
|
||||
elif(choice==modes[6]):
|
||||
elif(choice==modes[6]) or (choice==modes[10]) or (choice==modes[12]):
|
||||
var2.set(1)
|
||||
if (choice==modes[12]):
|
||||
var4.set(0.2)
|
||||
else:
|
||||
var4.set(0.7)
|
||||
var3.set(1)
|
||||
countlabel['fg']="grey"
|
||||
positlabel['fg']="grey"
|
||||
firstflabel['fg']="green"
|
||||
@@ -429,6 +662,8 @@ def Enableadvanced():
|
||||
firstframes['state']=NORMAL
|
||||
killframe['state']=NORMAL
|
||||
elif(choice==modes[5]):
|
||||
var2.set(1)
|
||||
var4.set(0.7)
|
||||
countlabel['fg']="grey"
|
||||
positlabel['fg']="grey"
|
||||
firstflabel['fg']="green"
|
||||
@@ -437,7 +672,12 @@ def Enableadvanced():
|
||||
Positframe['state']=DISABLED
|
||||
firstframes['state']=NORMAL
|
||||
killframe['state']=DISABLED
|
||||
elif(choice==modes[1]):
|
||||
elif(choice==modes[1]) or (choice==modes[11]):
|
||||
var2.set(1)
|
||||
if (choice==modes[11]):
|
||||
var3.set(0.5)
|
||||
else:
|
||||
var3.set(1)
|
||||
countlabel['fg']="grey"
|
||||
positlabel['fg']="grey"
|
||||
firstflabel['fg']="green"
|
||||
@@ -447,6 +687,8 @@ def Enableadvanced():
|
||||
firstframes['state']=NORMAL
|
||||
killframe['state']=DISABLED
|
||||
elif(choice==modes[2]):
|
||||
var2.set(1)
|
||||
var4.set(0.7)
|
||||
countlabel['fg']="grey"
|
||||
positlabel['fg']="green"
|
||||
firstflabel['fg']="green"
|
||||
@@ -455,8 +697,9 @@ def Enableadvanced():
|
||||
Positframe['state']=NORMAL
|
||||
firstframes['state']=NORMAL
|
||||
killframe['state']=NORMAL
|
||||
else:
|
||||
var1.set("1")
|
||||
elif(choice==modes[0]) or (choice==modes[3]) or (choice==modes[4]):
|
||||
var2.set(1)
|
||||
var4.set(0.7)
|
||||
countlabel['fg']="green"
|
||||
positlabel['fg']="green"
|
||||
firstflabel['fg']="green"
|
||||
@@ -475,16 +718,21 @@ def Disableadvanced():
|
||||
Positframe['state']=DISABLED
|
||||
firstframes['state']=DISABLED
|
||||
killframe['state']=DISABLED
|
||||
def refresh(event):
|
||||
if var=="0":
|
||||
pass
|
||||
else:
|
||||
modechoices['values']=toggle()
|
||||
def info():
|
||||
messagebox.showinfo("Help",
|
||||
"Datamosher Pro is made for those who want to datamosh their video files and achieve that glitch effect."
|
||||
"\nHow To Use:\n➤First input the video you want to datamosh."
|
||||
"Datamosher Pro is a tool that can mosh and corrupt video files and returns an awesome glitched video"
|
||||
"\nHow To Use:\n➤First open the video you want to datamosh."
|
||||
"\n➤Choose the desired datamosh mode, then select the export format."
|
||||
"\n➤Use advance options for different modes (For more info about the advanced options, go to our Github page)."
|
||||
"\n➤Then just click on the datamosh button, then wait for a few seconds."
|
||||
"\n➤After converting, your video will be moshed and you can find the video in the directory."
|
||||
"\n➤Note that if you mosh the same files in the same location again, then the new moshed file will replace the old file."
|
||||
"\n\nDeveloper: Akash Bora (a.k.a. Akascape)\nIf you have any issue then contact me on Github.")
|
||||
"\n➤Use advance options for different modes (For more details about the advanced options, visit our Github page)."
|
||||
"\n➤Click on the datamosh button, then wait for a few seconds."
|
||||
"\n➤After conversion, your video will be ready and you can view the datamoshed video inside its directory."
|
||||
"\n\nDeveloper: Akash Bora (a.k.a. Akascape)\nIf you are facing any issue then contact me on Github."
|
||||
"\nVersion-1.3")
|
||||
def callback(url):
|
||||
webbrowser.open_new_tab("https://github.com/Akascape/Datamosher-Pro-GUI-.git")
|
||||
root=Tk()
|
||||
@@ -501,47 +749,49 @@ headlabel=Label(root,image=LabelIMG,borderwidth=0, highlightthickness=0, padx=0,
|
||||
headlabel.grid()
|
||||
LocationError=Label(root,text="Choose Video To Datamosh",fg="red",bg='#FFFFFF')
|
||||
LocationError.grid()
|
||||
OpeningFile= Button(root, width=60,bg="#82CC6C",fg="white", text="OPEN",highlightthickness=1,borderwidth=0.2,relief="groove",command=openfile)
|
||||
OpeningFile= Button(root, width=61,bg="#82CC6C",fg="white", text="OPEN",highlightthickness=1,borderwidth=0.2,relief="groove",command=openfile)
|
||||
OpeningFile.grid()
|
||||
chooselabel=Label(root,text="Select Mode:",font="Aharoni",bg='#FFFFFF')
|
||||
chooselabel.place(x=80,y=115,anchor='center')
|
||||
modes=["Bloom","Invert","Jiggle", "Overlap","Pulse", "Reverse", "Random", "Classic"]
|
||||
modechoices=ttk.Combobox(root,values=modes, font="Verdana 12", width=7)
|
||||
modes=["Bloom", "Invert", "Jiggle", "Overlap", "Pulse", "Reverse", "Random", "Classic", "Glide", "Sort", "Shake", "Echo", "Void"]
|
||||
modechoices=ttk.Combobox(root,values=modes, font="Verdana 12", width=7, height=13)
|
||||
modechoices.current(0)
|
||||
modechoices.bind('<FocusIn>', lambda event: refresh(event))
|
||||
modechoices.place(x=130,y=104)
|
||||
exportchoices=["AVI(RAW)","MP4","GIF","MOV","MKV","WMV"]
|
||||
exportchoices=["AVI","MP4","GIF","MOV","MKV","WMV"]
|
||||
exportlabel=Label(root,text="Export Format:",font="Aharoni",bg='#FFFFFF')
|
||||
exportlabel.place(x=270,y=105)
|
||||
exportbox=ttk.Combobox(root,values=exportchoices, font="Verdana 10", width=8)
|
||||
exportbox.current(1)
|
||||
exportbox.place(x=380,y=104)
|
||||
exportbox.place(x=380,y=106)
|
||||
global var
|
||||
var = IntVar()
|
||||
var.set(0)
|
||||
advancedbox=Checkbutton(root, text="Advanced",bg="#FFFFFF", command=toggle,variable=var,onvalue=1, offvalue=0)
|
||||
advancedbox.place(x=70,y=150, anchor='center')
|
||||
var1=IntVar()
|
||||
var1=DoubleVar()
|
||||
var1.set(1)
|
||||
countlabel=Label(root,text="Glitch Freqency",fg="grey",bg='#FFFFFF')
|
||||
countlabel.place(x=40,y=165)
|
||||
Countframe=Entry(root,bg="light blue",width=10,borderwidth=3, textvariable=var1,state=DISABLED)
|
||||
countlabel=Label(root,text="Glitch Size",fg="grey",bg='#FFFFFF')
|
||||
countlabel.place(x=50,y=165)
|
||||
Countframe=Entry(root,bg="#00D2FF",width=10,borderwidth=3, textvariable=var1,state=DISABLED)
|
||||
Countframe.place(x=140,y=165)
|
||||
var2=IntVar()
|
||||
var2=DoubleVar()
|
||||
var2.set(1)
|
||||
positlabel=Label(root,text="Frames Frequency",fg="grey",bg='#FFFFFF')
|
||||
positlabel.place(x=270,y=165)
|
||||
Positframe=Entry(root,bg="light blue",width=10,borderwidth=3, textvariable=var2, state=DISABLED)
|
||||
Positframe=Entry(root,bg="#00D2FF",width=10,borderwidth=3, textvariable=var2, state=DISABLED)
|
||||
Positframe.place(x=380,y=165)
|
||||
var3=IntVar()
|
||||
var3.set(1)
|
||||
firstflabel=Label(root,text="Ignored Frames",fg="grey",bg='#FFFFFF')
|
||||
firstflabel.place(x=40,y=200)
|
||||
firstframes=Entry(root,bg="light blue",width=10,borderwidth=3, textvariable=var3, state=DISABLED)
|
||||
firstflabel=Label(root,text="First Frame",fg="grey",bg='#FFFFFF')
|
||||
firstflabel.place(x=50,y=200)
|
||||
firstframes=Entry(root,bg="#00D2FF",width=10,borderwidth=3, textvariable=var3, state=DISABLED)
|
||||
firstframes.place(x=140,y=200)
|
||||
var4=DoubleVar()
|
||||
var4.set(0.7)
|
||||
killlabel=Label(root,text="Kill Frames",fg="grey",bg='#FFFFFF')
|
||||
killlabel.place(x=270,y=200)
|
||||
killframe=Entry(root,bg="light blue",width=10,borderwidth=3, textvariable=var4, state=DISABLED)
|
||||
killframe=Entry(root,bg="#00D2FF",width=10,borderwidth=3, textvariable=var4, state=DISABLED)
|
||||
killframe.place(x=380,y=200)
|
||||
global Wait
|
||||
Wait=Label(root,text="",fg="#6D76CD",bg='#FFFFFF', font=("Aharoni",15))
|
||||
@@ -559,4 +809,4 @@ datamoshbtn=Button(root,image=buttonIMG,borderwidth=0, highlightthickness=0, pad
|
||||
datamoshbtn.place(x=100,y=270)
|
||||
root.mainloop()
|
||||
#DEVELOPER: AKASH BORA (a.k.a Akascape)
|
||||
#Version=1.0
|
||||
#Version=1.3
|
||||
@@ -1,6 +1,9 @@
|
||||
#Read this to use the python version.
|
||||
This version is comparitively lower in size and works the same inside python environment. To use this version, just download the zip file and extract this folder and then double click on the Datamosher Pro v1.1.py to open the program, then use the software as describes in the Readme.md or click the "i" button inside the software.
|
||||
Note that you must have the required modules installed. Most of the modules that are used are installed by default, but only one module you need to install separately is Imageio.
|
||||
To install that module, open CMD or other terminal and type "pip install Imageio" and "pip install Imageio-ffmpeg, then it will be installed and you can open Datamosher Pro v1.1.py. Also change the directory of ffmpeg if needed (see the comments)
|
||||
Do not delete or move any assets that are linked with the main file or else it will show error.
|
||||
So, this is all about the python version, you can now Datamosh your videos!
|
||||
This version is comparitively lower in size and works the same inside python environment.
|
||||
To use this version, just download the zip file and extract this folder and then double
|
||||
click on the Datamosher Pro v1.2.py to open the program, then use the software as describes in the Readme.md
|
||||
or click the "i" button inside the software.
|
||||
In this version, the modules will be automatically downloaded and ffmpeg path will be pre-specified, so no need to worry about modules and stuff
|
||||
(specially for windows user). Make sure to restart the program after completing the automatic module installation.
|
||||
Do not delete or move any assets or folder that are linked with the main file or else it will show error.
|
||||
This is all about the python version, you can now Datamosh your videos!
|
||||
|
||||
27
Python Version/pymosh/__init__.py
Normal file
27
Python Version/pymosh/__init__.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from .container import avi
|
||||
|
||||
__all__ = ['Index']
|
||||
|
||||
|
||||
class Index(object):
|
||||
"""Index is an index of video frame data."""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def from_file(filename: str):
|
||||
instance = Index()
|
||||
instance.filename = filename
|
||||
instance.index = None
|
||||
|
||||
# Assume AVI for now
|
||||
instance.index = avi.AVIFile.from_file(filename)
|
||||
|
||||
return instance
|
||||
|
||||
def __getattr__(self, index):
|
||||
return getattr(self.index, index)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.index)
|
||||
1
Python Version/pymosh/codec/__init__.py
Normal file
1
Python Version/pymosh/codec/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
6
Python Version/pymosh/codec/mpeg4.py
Normal file
6
Python Version/pymosh/codec/mpeg4.py
Normal file
@@ -0,0 +1,6 @@
|
||||
IFRAME_HEADER = b'\x00\x00\x01\xb0'
|
||||
|
||||
|
||||
def is_iframe(frame):
|
||||
"""Determine whether frame is an I frame."""
|
||||
return frame[:4] == IFRAME_HEADER
|
||||
1
Python Version/pymosh/container/__init__.py
Normal file
1
Python Version/pymosh/container/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
124
Python Version/pymosh/container/avi.py
Normal file
124
Python Version/pymosh/container/avi.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import struct
|
||||
|
||||
from pymosh.codec.mpeg4 import is_iframe
|
||||
|
||||
from . import riff
|
||||
|
||||
|
||||
class AVIFile(object):
|
||||
"""A wrapper for AVI files."""
|
||||
|
||||
def __init__(self):
|
||||
self.riff = riff.RiffIndex()
|
||||
self.streams = []
|
||||
self.frame_order = []
|
||||
|
||||
@staticmethod
|
||||
def from_file(filename: str):
|
||||
instance = AVIFile()
|
||||
|
||||
instance.riff = riff.RiffIndex.from_file(filename=filename)
|
||||
|
||||
header = instance.riff.find(b'LIST', b'hdrl')
|
||||
# Get stream info
|
||||
stream_lists = header.find_all(b'LIST', b'strl')
|
||||
for l in stream_lists:
|
||||
strh = l.find(b'strh')
|
||||
data = strh.data
|
||||
fccType, = struct.unpack(b'4s', data[:4])
|
||||
stream = Stream(len(instance.streams), fccType)
|
||||
instance.streams.append(stream)
|
||||
|
||||
instance.split_streams()
|
||||
|
||||
return instance
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.streams)
|
||||
|
||||
def add_frame(self, chunk):
|
||||
stream_num = int(chunk.header[:2])
|
||||
if stream_num < len(self.streams):
|
||||
self.frame_order.append(
|
||||
(stream_num, len(self.streams[stream_num])))
|
||||
self.streams[stream_num].add_frame(chunk)
|
||||
|
||||
def split_streams(self):
|
||||
movi = self.riff.find(b'LIST', b'movi')
|
||||
for chunk in movi:
|
||||
self.add_frame(chunk)
|
||||
|
||||
def combine_streams(self):
|
||||
chunks = []
|
||||
for frame_record in self.frame_order:
|
||||
stream_num, frame_num = frame_record
|
||||
stream = self.streams[stream_num]
|
||||
frame = stream[frame_num]
|
||||
chunks.append(frame)
|
||||
return chunks
|
||||
|
||||
def _video(self):
|
||||
return filter(lambda stream: stream.type == b'vids', self.streams)
|
||||
video = property(_video)
|
||||
|
||||
def _audio(self):
|
||||
return filter(lambda stream: stream.type == b'auds', self.streams)
|
||||
audio = property(_audio)
|
||||
|
||||
def rebuild(self):
|
||||
"""Rebuild RIFF tree and index from streams."""
|
||||
movi = self.riff.find(b'LIST', b'movi')
|
||||
movi.chunks = self.combine_streams()
|
||||
self.rebuild_index()
|
||||
|
||||
def rebuild_index(self):
|
||||
old_index = self.riff.find(b'idx1')
|
||||
movi = self.riff.find(b'LIST', b'movi')
|
||||
data = b''
|
||||
offset = 0
|
||||
flags = {
|
||||
'base': 0x00000000,
|
||||
'keyframe': 0x00000010,
|
||||
}
|
||||
for chunk in movi:
|
||||
length = len(chunk)
|
||||
frame_flags = flags['base']
|
||||
# If it's a video keyframe or audio frame, use keyframe flag
|
||||
if (chunk.header[2] == b'd' and is_iframe(chunk)) or (chunk.header[2] == b'w'):
|
||||
frame_flags |= flags['keyframe']
|
||||
data += struct.pack(b'<4sIII', chunk.header, frame_flags, offset,
|
||||
length+8)
|
||||
offset += length + 8 + (length % 2)
|
||||
new_index = riff.RiffDataChunk(b'idx1', data)
|
||||
self.riff.find(b'RIFF').replace(old_index, new_index)
|
||||
|
||||
def write(self, fh):
|
||||
self.riff.write(fh)
|
||||
|
||||
|
||||
class Stream(object):
|
||||
def __init__(self, num, stream_type):
|
||||
self.num = int(num)
|
||||
self.type = stream_type
|
||||
self.chunks = []
|
||||
|
||||
def add_frame(self, chunk):
|
||||
self.chunks.append(chunk)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.chunks.__getitem__(index)
|
||||
|
||||
def __iter__(self):
|
||||
return self.chunks.__iter__()
|
||||
|
||||
def __len__(self):
|
||||
return len(self.chunks)
|
||||
|
||||
def append(self, *args):
|
||||
return self.chunks.append(*args)
|
||||
|
||||
def extend(self, *args):
|
||||
return self.chunks.extend(*args)
|
||||
|
||||
def replace(self, chunks):
|
||||
self.chunks = chunks
|
||||
263
Python Version/pymosh/container/riff.py
Normal file
263
Python Version/pymosh/container/riff.py
Normal file
@@ -0,0 +1,263 @@
|
||||
import os
|
||||
import struct
|
||||
from io import IOBase
|
||||
|
||||
list_headers = (b'RIFF', b'LIST')
|
||||
|
||||
|
||||
class UnexpectedEOF(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RiffIndexChunk(object):
|
||||
def __init__(self, fh, header, length, position):
|
||||
self.file = fh
|
||||
self.header = header
|
||||
self.length = int(length)
|
||||
self.position = position
|
||||
|
||||
def __str__(self):
|
||||
return str(self.bytes())
|
||||
|
||||
def bytes(self) -> bytes:
|
||||
return self.header + struct.pack('<I', self.length) + self.data
|
||||
|
||||
def __len__(self):
|
||||
return self.length
|
||||
|
||||
def __getslice__(self, start, end):
|
||||
if start is None:
|
||||
start = 0
|
||||
if end is None:
|
||||
end = self.length
|
||||
|
||||
current = self.file.tell()
|
||||
self.file.seek(self.position+start)
|
||||
if start < end and start <= self.length:
|
||||
if end > self.length:
|
||||
end = self.length
|
||||
data = self.file.read(end-start)
|
||||
self.file.seek(current)
|
||||
return data
|
||||
else:
|
||||
return ''
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, slice):
|
||||
return self.__getslice__(index.start, index.stop)
|
||||
return self[index:index+1]
|
||||
|
||||
def _data(self):
|
||||
"""Read data from the file."""
|
||||
current_position = self.file.tell()
|
||||
self.file.seek(self.position)
|
||||
data = self.file.read(self.length)
|
||||
self.file.seek(current_position)
|
||||
if self.length % 2:
|
||||
data += b'\x00' # Padding byte
|
||||
return data
|
||||
data = property(_data)
|
||||
|
||||
def as_data(self):
|
||||
"""Return a RiffDataChunk read from the file."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class RiffIndexList(RiffIndexChunk):
|
||||
def __init__(self, header, list_type, *args, **kwargs):
|
||||
self.header = header
|
||||
self.type = list_type
|
||||
self.file = kwargs.get('file', None)
|
||||
self.position = kwargs.get('position', 0)
|
||||
self.chunks = kwargs.get('chunks', [])
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.chunks[index]
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
return self.chunks.__setitem__(index, value)
|
||||
|
||||
def __delitem__(self, index):
|
||||
return self.chunks.__delitem__(index)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.chunks)
|
||||
|
||||
def __len__(self):
|
||||
"""Return total data length of the list and its headers."""
|
||||
return self.chunk_length() + len(self.type) + len(self.header) + 4
|
||||
|
||||
def chunk_length(self):
|
||||
length = 0
|
||||
for chunk in self.chunks:
|
||||
chunk_len = len(chunk)
|
||||
length += chunk_len + 8 # Header and length bytes
|
||||
length += chunk_len % 2 # Pad byte
|
||||
return length
|
||||
|
||||
def __str__(self):
|
||||
return str(self.bytes())
|
||||
|
||||
def bytes(self) -> bytes:
|
||||
"""Returns a byte representation of the chunk."""
|
||||
length_bytes = struct.pack('<I', self.chunk_length() + len(self.type))
|
||||
return self.header + length_bytes + self.type
|
||||
|
||||
class NotFound(Exception):
|
||||
"""Indicates a chunk or list was not found by the find method."""
|
||||
pass
|
||||
|
||||
def find(self, header, list_type=None):
|
||||
"""Find the first chunk with specified header and optional list type."""
|
||||
for chunk in self:
|
||||
if chunk.header == header and (list_type is None or (header in
|
||||
list_headers and chunk.type == list_type)):
|
||||
return chunk
|
||||
elif chunk.header in list_headers:
|
||||
try:
|
||||
result = chunk.find(header, list_type)
|
||||
return result
|
||||
except chunk.NotFound:
|
||||
pass
|
||||
if list_type is None:
|
||||
raise self.NotFound('Chunk \'{0}\' not found.'.format(header))
|
||||
else:
|
||||
raise self.NotFound('List \'{0} {1}\' not found.'.format(header,
|
||||
list_type))
|
||||
|
||||
def find_all(self, header, list_type=None):
|
||||
"""Find all direct children with header and optional list type."""
|
||||
found = []
|
||||
for chunk in self:
|
||||
if chunk.header == header and (not list_type or (header in
|
||||
list_headers and chunk.type == list_type)):
|
||||
found.append(chunk)
|
||||
return found
|
||||
|
||||
def replace(self, child, replacement):
|
||||
"""Replace a child chunk with something else."""
|
||||
for i in range(len(self.chunks)):
|
||||
if self.chunks[i] == child:
|
||||
self.chunks[i] = replacement
|
||||
|
||||
def remove(self, child):
|
||||
"""Remove a child element."""
|
||||
for i in range(len(self)):
|
||||
if self[i] == child:
|
||||
del self[i]
|
||||
|
||||
|
||||
class RiffDataChunk(object):
|
||||
"""A RIFF chunk with data in memory instead of a file."""
|
||||
|
||||
def __init__(self, header, data):
|
||||
self.header = header
|
||||
self.length = len(data)
|
||||
self.data = data
|
||||
|
||||
@staticmethod
|
||||
def from_data(data):
|
||||
"""Create a chunk from data including header and length bytes."""
|
||||
header, _ = struct.unpack('4s<I', data[:8])
|
||||
data = data[8:]
|
||||
return RiffDataChunk(header, data)
|
||||
|
||||
def bytes(self) -> bytes:
|
||||
"""Returns a byte array representation of the chunk."""
|
||||
return self.header + struct.pack('<I', self.length) + self.data
|
||||
|
||||
def __str__(self):
|
||||
return str(self.bytes())
|
||||
|
||||
def __len__(self):
|
||||
return self.length
|
||||
|
||||
def __getslice__(self, start, end):
|
||||
return self.data[start:end]
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.data[index]
|
||||
|
||||
|
||||
class RiffIndex(RiffIndexList):
|
||||
def __init__(self):
|
||||
self.file = None
|
||||
self.chunks = []
|
||||
|
||||
@staticmethod
|
||||
def from_file(filename: str):
|
||||
instance = RiffIndex()
|
||||
|
||||
instance.file = open(filename, 'rb')
|
||||
instance.size = instance.get_size()
|
||||
instance.scan_file()
|
||||
|
||||
return instance
|
||||
|
||||
def write(self, fh: IOBase) -> None:
|
||||
def print_chunks(chunks):
|
||||
for chunk in chunks:
|
||||
fh.write(chunk.bytes())
|
||||
if chunk.header in (b'RIFF', b'LIST'):
|
||||
print_chunks(chunk.chunks)
|
||||
|
||||
print_chunks(self.chunks)
|
||||
|
||||
def get_size(self):
|
||||
current = self.file.tell()
|
||||
self.file.seek(0, 2)
|
||||
size = self.file.tell()
|
||||
self.file.seek(current)
|
||||
return size
|
||||
|
||||
def readlen(self, length):
|
||||
buf = self.file.read(length)
|
||||
if len(buf) == length:
|
||||
return buf
|
||||
else:
|
||||
raise UnexpectedEOF(
|
||||
'End of file reached after {0} bytes.'.format(len(buf)))
|
||||
|
||||
def scan_file(self):
|
||||
header = self.readlen(4)
|
||||
if header == b'RIFF':
|
||||
length, list_type = struct.unpack('<I4s', self.readlen(8))
|
||||
chunks = self.scan_chunks(length-4)
|
||||
self.chunks.append(RiffIndexList(header, list_type, file=self.file,
|
||||
position=0, chunks=chunks))
|
||||
else:
|
||||
raise Exception('Not a RIFF file!')
|
||||
|
||||
def scan_chunks(self, data_length):
|
||||
chunks = []
|
||||
total_length = 0
|
||||
while total_length < data_length:
|
||||
header = self.readlen(4)
|
||||
total_length += 4
|
||||
|
||||
length, = struct.unpack('<I', self.file.read(4))
|
||||
total_length += length + 4 # add 4 for itself
|
||||
|
||||
position = self.file.tell()
|
||||
|
||||
if header in list_headers:
|
||||
list_type = self.readlen(4)
|
||||
data = self.scan_chunks(length-4)
|
||||
if length % 2:
|
||||
# Padding byte
|
||||
self.file.seek(1, os.SEEK_CUR)
|
||||
total_length += 1
|
||||
chunks.append(RiffIndexList(header, list_type, file=self.file,
|
||||
position=position, chunks=data))
|
||||
else:
|
||||
self.file.seek(length, os.SEEK_CUR)
|
||||
if length % 2:
|
||||
# Padding byte
|
||||
self.file.seek(1, os.SEEK_CUR)
|
||||
total_length += 1
|
||||
chunks.append(RiffIndexChunk(
|
||||
self.file, header, length, position))
|
||||
return chunks
|
||||
|
||||
def close(self):
|
||||
self.file.close()
|
||||
75
Readme.md
75
Readme.md
@@ -1,47 +1,50 @@
|
||||
# Datamosher Pro
|
||||
<br>Datamoshing is an effect that really looks cool and if you also want to make this glitch effect with your videos, you are in the right place!
|
||||

|
||||
<br><b>Datamoshing is a cool video effect and if you also want to create this glitch effect with your videos, you are in the right place!</b>
|
||||
<br><p align='center'><img src="https://user-images.githubusercontent.com/89206401/141642297-7c62cf6f-7024-430f-88a2-c9cbbf0dc655.png"></p>
|
||||
<br>➤Why I made this?
|
||||
<br>I was also looking for good datamoshing softwares, you can either have to use those old softwares like Avidemux or have to look for some paid plugins, but I created my own GUI based application that is Datamosher Pro which is a free project. It contains 7 different effects and more will be added in future, you can also help to make new effects. With Datamosher Pro, you can quickly and easily datamosh your videos(supports mp4, gif, avi, mov etc).
|
||||
<br>
|
||||
<br>➤How to Install?
|
||||
<br>You can either use the python based version for viewing logs and changing source code if you want, but if you are looking for faster renders then download the executable version of Datamosher Pro from the release page: (https://github.com/Akascape/Datamosher-Pro/releases/tag/Datamosher_Prov1.1.exe)
|
||||
<br>There is no malware or difference in the exe version(as the same python version is converted to .exe using Auto-Py-To-Exe Converter).
|
||||
<br>Note: For python users, make sure you have all the assets with the python file, Imageio and Imageio-ffmpeg module installed in your system.
|
||||
<br>
|
||||
<br>➤How to Use?
|
||||
<br>•Input the video file first (supported formats- mp4, gif, avi + more will be added if you demand")
|
||||
<br>I was also looking for some good datamoshing software and I found that you have to either use those old softwares like Avidemux or have to look for some paid plugins, so I created my own GUI based application that is Datamosher Pro which is a free project. It contains 13 different effects which can replicate any type of datamoshing style. With Datamosher Pro, you can quickly and easily datamosh your videos (supports mp4, gif, avi, mkv etc).
|
||||
# How to Install?
|
||||
You can either use the python based version for viewing logs and changing source code if you want, but if you are looking for faster renders then download the windows executable version of Datamosher Pro from the release page:
|
||||
<br>[⬇️DOWNLOAD⬇️](https://github.com/Akascape/Datamosher-Pro/releases/tag/Datamosher_Prov1.3.exe)
|
||||
<br>There is no malware or difference in the exe version as the same python version is converted to .exe using Auto-Py-To-Exe Converter.
|
||||
<br>Note: For python users, make sure you have all the assets saved in the same folder. Modules will be automatically downloaded if not installed, so no need to worry.
|
||||
# How to Use?
|
||||
•Input the video file first (supported formats- mp4, gif, avi + more will be added if you demand")
|
||||
<br>•Choose the desired datamosh mode, then select the export format"
|
||||
<br>•Use advance options to get more accurate results"
|
||||
<br>•Then just click on the datamosh button, then wait for a few seconds"
|
||||
<br>•Click on the datamosh button, then wait for a few seconds"
|
||||
<br>•Then your video will be moshed, see the video in the directory"
|
||||
<br>•Note that if you mosh the same files in the same location again, then the new moshed file will replace the old file.
|
||||
<br>
|
||||
<br>➤Effects Info:
|
||||
<br>(c is Glitch Frequency and n is Frame Frequency)
|
||||
<br>Classic - uses classic ffmpeg way to corrupt videos and remove pixels (NEW)
|
||||
<br>Random - randomizes frame order
|
||||
<br>Reverse - reverse frame order
|
||||
<br>Invert - switches each consecutive frame witch each other
|
||||
<br>Bloom - duplicates c times p-frame number n (also known as waterbloom)
|
||||
<br>Pulse - duplicates groups of c p-frames every n frames (My fav)
|
||||
<br>Overlap - copy group of c frames taken from every nth position
|
||||
<br>Jiggle - take frame from around current position. n parameter is spread size.
|
||||
<br>NOTE: audio glitching is not available for all modes!
|
||||
# Effects Info:
|
||||
c=Glitch Size; n=Frame Frequency
|
||||
<b>
|
||||
<br>All effects:
|
||||
<br>•Glide - duplicates number of n frames and show it as a flow before reaching the p-frame (NEW)
|
||||
<br>•Sort - sorts video frames by data size in a rapid movement (NEW)
|
||||
<br>•Echo - duplicates the single video and apply the mosh in the midpoint (NEW)
|
||||
<br>•Shake - shakes the pixel movement throughout the video (NEW)
|
||||
<br>•Classic - uses the traditional ffmpeg way to change the files and then remove the i-frames
|
||||
<br>•Random - randomizes frame order
|
||||
<br>•Reverse - reverse frame order
|
||||
<br>•Invert - switches each consecutive frame witch each other
|
||||
<br>•Bloom - duplicates c times p-frame number n
|
||||
<br>•Pulse - duplicates groups of c p-frames every n frames
|
||||
<br>•Overlap - copy group of c frames taken from every nth position
|
||||
<br>•Jiggle - take frame from around current position. n parameter is spread size.
|
||||
<br>•Void - gives a clean output but with distorted pixels
|
||||
</b>
|
||||
<br>
|
||||
<br>➤How to use Advanced Options?
|
||||
<br>The advanced tab is very useful and you can use it to get accurate results.
|
||||
<br>Glitch Frequency - tells how often to glitch (for modes that support it)
|
||||
<br>Frame Frequency - tells how many frames in the glitch (for modes that support it)
|
||||
<br>Ignored First Frame - tells whether to keep first video frames
|
||||
<br>Kill frames - tells max framesize to kill while cleaning
|
||||
<br>You can try around changing the values from 50-100 and see the results, you can visit https://github.com/itsKaspar/tomato.git to view more examples about the advanced tab.
|
||||
<br>
|
||||
<br>➤User Interface:
|
||||
<br>
|
||||
<br>
|
||||
<br>•Glitch Size - tells how often to glitch (for modes that support it)
|
||||
<br>•Frame Frequency - tells how many frames to apply in the glitch (for modes that support it)
|
||||
<br>•First Frame - tells whether to keep first video frames
|
||||
<br>•Kill frames - tells max framesize to kill while cleaning
|
||||
<br>You can try experimenting with the values and see the results.
|
||||
# User Interface:
|
||||
<br><img src="https://user-images.githubusercontent.com/89206401/141643709-73b68644-5697-4c39-8be0-5384dc391fa4.png">
|
||||
<br>➤More Info about this project:
|
||||
<br>The effects are inspired from ItsKaspar's tomato.py which can only handle .avi file structure, but in Datamosher Pro you can use any video file type including mp4, avi, gif, mov, mkv +more. The files are first converted to avi file using Imageio without losing any quality and then the effect is applied and then again the corrupted file is converted to stable version using the same process so that the output video can be directly used by other softwares without any error. A raw form option is also available if needed. You will not find this type of GUI program anywhere, I hope there is no error in the software but if you saw any bug then raise an issue. All the logo and designs are created by me. <br>Thanks! Made by Akash Bora (a.k.a Akascape).
|
||||
<br>The effects are inspired from ItsKaspar's [tomato.py](https://github.com/itsKaspar/tomato) and Joe Friedl's [pymosh](https://github.com/grampajoe/pymosh) which can only handle .avi file structures, but in Datamosher Pro you can use any video file type including mp4, avi, gif, mov, mkv +more. The files are first converted to avi file using Imageio-ffmpeg without losing much quality and then the effect is applied and then again the corrupted file is converted to stable version using the same process so that the output video can be directly used in other softwares for editing without any error. A raw file option is also available if needed. You will not find this type of GUI program anywhere with so many free effects, I hope there is no error in the program but if you find any bug then raise an issue. You can also help to make new datamosh effects. All the logo and designs are created by me. <br>Thanks! Made by Akash Bora (a.k.a Akascape).
|
||||
<br>
|
||||
<br>DATAMOSH MADE EASY!
|
||||
<br>Current Version=1.1
|
||||
|
||||
<br>Current Version=1.3
|
||||
|
||||
Reference in New Issue
Block a user