Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import tkinter as tk
- from tkinter.filedialog import askopenfilename, askdirectory
- from tkinter.messagebox import showerror
- from enum import Enum, auto
- import subprocess
- import os
- import sys
- import pathlib
- import json
- import threading
- import re
- from tempfile import TemporaryDirectory
- # Changelog
- # 1.0 - further tweaked filename parsing so hevc files with no description don't get left behind when convert and delete is enabled
- # beta9 - added sessionid to cookie flag; minor improvement to cleanup (long filenames shouldn't leave behind hevc files when convert and delete is enabled)
- # beta8 - downloading multiple ids now uses a temp file instead of pipes
- # beta7 - redownloading with rename sd option now deletes the new file instead of throwing an error when failing to rename because of file already existing
- # beta6 - rename sd option added
- # beta5 - date in filename option added
- # beta4 - adjusted how ids are added to the command (user stories may not have been working)
- # beta3 - user agent field added
- # beta2 - any errors while running videos-node will now dump stderr to an alert dialog; max file length has been renamed to be more indicative of what it does
- # beta1 - init
- class Consts:
- SAVE_CFG_ON_EXIT = True
- SAVE_IDS_FIELD_ON_SAVE_CFG = False
- SHOW_TOOLTIPS = True
- # AwemerWrapper
- IGNORE_ALREADY_DOWNLOADED = True
- IGNORE_EMPTY_DOWNLOADS = True
- HEVC_SFX = "~hevc"
- CONV_SFX = "~h264"
- SD_SFX = "~sd"
- MAX_RETRIES = 10
- MAX_CONVERTS = 10
- MAX_DOWNLOADS = 1
- # UI
- LABEL_SIZE = 8
- DEFAULT_PAD = 4
- class AwemerWrapper:
- def __init__(self):
- self._ids = None
- self._hq = None
- self._add_date = None
- self._verify = None
- self._convert = None
- self._delete = None
- self._rm_sd_suffix = None
- self._sid = None
- self._uid = None
- self._usr_agent = None
- self._rand_usr_agent = None
- self._max_results = None
- self._max_fname_len = None
- self._save_dir = None
- self._awemer_loc = None
- self._additional_flags = None
- self._t_param = None
- self._tmp_ids_dir = None
- def with_ids(self, ids):
- self._ids = ids.strip().replace('\r', '').replace('\n', ' ')
- return self
- def with_hq(self, hq):
- self._hq = hq
- return self
- def with_add_date(self, add_date):
- self._add_date = add_date
- return self
- def with_verify(self, verify):
- self._verify = verify
- return self
- def with_convert(self, convert):
- self._convert = convert
- return self
- def with_delete(self, delete):
- self._delete = delete
- return self
- def with_rm_sd_suffix(self, rm_sd_suffix):
- self._rm_sd_suffix = rm_sd_suffix
- return self
- def with_sid(self, sid):
- self._sid = sid.strip()
- return self
- def with_uid(self, uid):
- self._uid = uid.strip()
- return self
- def with_usr_agent(self, usr_agent):
- self._usr_agent = usr_agent.strip()
- return self
- def with_random_usr_agent(self, rand_usr_agent):
- self._rand_usr_agent = rand_usr_agent
- return self
- def with_max_results(self, max_results):
- self._max_results = max_results.strip()
- return self
- def with_max_filename_len(self, max_fname_len):
- self._max_fname_len = max_fname_len.strip()
- return self
- def with_save_dir(self, save_dir):
- self._save_dir = save_dir.strip()
- return self
- def with_awemer(self, awemer_loc):
- self._awemer_loc = awemer_loc.strip()
- return self
- def _file_ends_with(self, ext, f):
- try:
- return f.split(".")[-2].endswith(ext)
- except:
- return False
- def _get_id(self, fname):
- return fname.split(".")[0].split("~")[0].split(" ")[0]
- def _cleanup_specific(self, suffix, filenames):
- for f in filter(lambda f: self._file_ends_with(suffix, f), filenames):
- try:
- parts = f.split(".")
- parts[-2] = parts[-2].removesuffix(suffix).rstrip()
- renamed = ".".join(parts)
- os.rename(self._save_dir + "/" + f, self._save_dir + "/" + renamed)
- print(f + " -> " + renamed)
- except FileExistsError:
- os.remove(self._save_dir + "/" + f)
- print("File exists, removing " + f)
- def _cleanup(self):
- if self._convert and self._delete:
- print("Cleaning tp HEVC")
- filenames = next(os.walk(self._save_dir), (None, None, []))[2]
- ids = [self._get_id(f) for f in filenames if self._file_ends_with(Consts.CONV_SFX, f)]
- for f in filter(lambda f: self._file_ends_with(Consts.HEVC_SFX, f), filenames):
- if self._get_id(f) in ids:
- print("Converted h264 exists, removing HEVC " + f)
- os.remove(self._save_dir + "/" + f)
- else:
- print("MISSING HEVC CONVERSION FOR " + f)
- print("Cleaning up H264 suffixes")
- filenames = next(os.walk(self._save_dir), (None, None, []))[2]
- self._cleanup_specific(Consts.CONV_SFX, filenames)
- if self._rm_sd_suffix:
- print("Cleaning up SD filenames")
- filenames = next(os.walk(self._save_dir), (None, None, []))[2]
- self._cleanup_specific(Consts.SD_SFX, filenames)
- if self._tmp_ids_dir != None:
- print("Cleaning up temp files")
- self._tmp_ids_dir.cleanup()
- def _exec(self, callback, err_callback):
- print("Assembling awemer command and executing")
- cmd = "node \"" + str(self._awemer_loc) + "\""
- if self._ids != None and self._ids != "":
- if re.findall("\\s", self._ids):
- # Temp files can be a little tricky on different systems -
- # may not be able to have multiple instances open at the same time and file disappears as soon as it is closed (node needs a filename to open, not a python file).
- # Using instead temp dir which can be opened here and deleted along with all its contents in _cleanup().
- self._tmp_ids_dir = TemporaryDirectory()
- fname = self._tmp_ids_dir.name + "/ids.txt"
- with open(fname, "w") as f:
- f.write(self._ids)
- cmd += " i=\"" + str(fname) + "\""
- # Alternatively this was the piping jankery without using temp files, but it might not work correcty for multiple ids at once.
- # # pipe jankery to get videos-node to read whitespace separated ids from stdin (requires shell=True)
- # cmd = "echo " + str(self._ids.replace('\r', '').replace('\n', ' ')) + " | " + cmd + " i="
- else:
- # single ids sometimes don't like getting piped, e.g. when scraping user stories (that needs a literal i=<id> param)
- cmd += " i=" + str(self._ids)
- elif self._uid != None:
- cmd += " i=" + str(self._uid)
- if self._hq:
- cmd += " q=2"
- else:
- cmd += " q=0"
- if self._add_date:
- cmd += " c"
- if self._verify:
- cmd += " verify_files"
- if self._convert:
- cmd += " convert_hevc"
- cmd += " rename_hevc"
- if self._sid != None and self._sid != "":
- cmd += " j=sid_tt=" + str(self._sid) + ";sessionid=" + str(self._sid)
- cmd += " cookie=" + str(self._sid)
- if self._rand_usr_agent:
- cmd += " ur"
- elif self._usr_agent != None and self._usr_agent != "":
- cmd += " u=\"" + str(self._usr_agent)+"\""
- if self._max_results != None and self._max_results != "":
- cmd += " l=" + str(self._max_results)
- if self._max_fname_len != None and self._max_fname_len != "":
- cmd += " d=" + str(self._max_fname_len)
- if self._save_dir != None and self._save_dir != "":
- cmd += " f=\"" + str(self._save_dir)+"\""
- if self._additional_flags != None and self._additional_flags != "":
- cmd += " " + str(self._additional_flags)
- if self._t_param != None and self._t_param != "":
- cmd += " t=" + str(self._t_param)
- cmd += " n=" + str(Consts.MAX_RETRIES) + " max_resumes=" + str(Consts.MAX_RETRIES) + " max_retries=" + str(Consts.MAX_RETRIES)
- cmd += " max_converts=" + str(Consts.MAX_CONVERTS) + " max_downloads=" + str(Consts.MAX_DOWNLOADS)
- if Consts.IGNORE_ALREADY_DOWNLOADED:
- cmd += " ignore_ids"
- if Consts.IGNORE_EMPTY_DOWNLOADS:
- cmd += " skip_empty"
- print("Executing:")
- print(cmd)
- error_lines = []
- # shell=False opens a terminal window while node runs, =True does not
- with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, shell=True) as p:
- for line in iter(p.stdout.readline, b''):
- callback(line.decode("utf-8").strip())
- for line in iter(p.stderr.readline, b''):
- error_lines.append(line.decode("utf-8").strip())
- self._cleanup()
- if len(error_lines) > 0:
- err_callback("\n".join(error_lines))
- callback("Done.")
- print("Done")
- # start downloading a specific type, the stdout will be returned in real time in the callback param, err_callback will be called at the end with all of stderr if there were errors
- def execute(self, download_type, callback, err_callback):
- match download_type:
- case DownloadType.VIDEOS:
- callback("Scraping videos...")
- self._t_param = "v"
- case DownloadType.STORIES:
- callback("Scraping stories...")
- self._t_param = "us"
- case DownloadType.IMAGES:
- callback("Scraping images...")
- self._t_param = "v"
- self._additional_flags = "images"
- case DownloadType.LIKES:
- callback("Scraping likes...")
- self._t_param = "ul"
- self._exec(callback, err_callback)
- class DownloadType(Enum):
- VIDEOS = auto()
- STORIES = auto()
- IMAGES = auto()
- LIKES = auto()
- ##### Config #####
- def get_datadir():
- """
- Returns a parent directory path
- where persistent application data can be stored.
- # linux: ~/.local/share
- # macOS: ~/Library/Application Support
- # windows: C:/Users/<USER>/AppData/Roaming
- """
- home = pathlib.Path.home()
- if sys.platform == "win32":
- return home / "AppData/Roaming"
- elif sys.platform.startswith("linux"):
- return home / ".local/share"
- elif sys.platform == "darwin":
- return home / "Library/Application Support"
- def save_config():
- print("Saving current config")
- cfg_dir = get_datadir()
- pathlib.Path(cfg_dir).mkdir(parents=True, exist_ok=True)
- cfg_file = cfg_dir / "awemepy_cfg.json"
- cfg = {}
- if Consts.SAVE_IDS_FIELD_ON_SAVE_CFG:
- cfg["ids"] = var_ids.get()
- cfg["max_vids"] = var_max_vids.get()
- cfg["max_file_len"] = var_max_file_len.get()
- cfg["sid"] = var_sid.get()
- cfg["uid"] = var_uid.get()
- cfg["usr_agent"] = var_usr_agent.get()
- cfg["rand_usr_agent"] = var_rand_usr_agent.get()
- cfg["save_dir"] = var_save_dir.get()
- cfg["awemer"] = var_awemer.get()
- cfg["hq"] = var_hq.get()
- cfg["add_date"] = var_add_date.get()
- cfg["verify"] = var_verify.get()
- cfg["convert"] = var_convert.get()
- cfg["delete"] = var_delete.get()
- cfg["rm_sd_suffix"] = var_rm_sd_suffix.get()
- json_str = json.dumps(cfg, indent=4)
- with open(cfg_file, "w") as f:
- f.write(json_str)
- def load_config():
- print("Checking if config exists and loading")
- cfg_dir = get_datadir()
- cfg_file = cfg_dir / "awemepy_cfg.json"
- if cfg_file.exists():
- lines = []
- with open(cfg_file, "r") as f:
- lines = f.readlines()
- json_str = "\n".join(lines)
- cfg = json.loads(json_str)
- if "ids" in cfg:
- var_ids.set(cfg["ids"])
- if "max_vids" in cfg:
- var_max_vids.set(cfg["max_vids"])
- if "max_file_len" in cfg:
- var_max_file_len.set(cfg["max_file_len"])
- if "sid" in cfg:
- var_sid.set(cfg["sid"])
- if "uid" in cfg:
- var_uid.set(cfg["uid"])
- if "usr_agent" in cfg:
- var_usr_agent.set(cfg["usr_agent"])
- if "rand_usr_agent" in cfg:
- var_rand_usr_agent.set(cfg["rand_usr_agent"])
- if "save_dir" in cfg:
- var_save_dir.set(cfg["save_dir"])
- if "awemer" in cfg:
- var_awemer.set(cfg["awemer"])
- if "hq" in cfg:
- var_hq.set(cfg["hq"])
- if "add_date" in cfg:
- var_add_date.set(cfg["add_date"])
- if "verify" in cfg:
- var_verify.set(cfg["verify"])
- if "convert" in cfg:
- var_convert.set(cfg["convert"])
- if "delete" in cfg:
- var_delete.set(cfg["delete"])
- if "rm_sd_suffix" in cfg:
- var_rm_sd_suffix.set(cfg["rm_sd_suffix"])
- ##### Main window parts #####
- def MainInputAndDownloadBtns(parent, var_ids, action_dl_vids, action_dl_stories, action_dl_imgs, action_dl_likes):
- container = tk.Frame(parent)
- ids = IdsInput(container, var_ids)
- btns = DownloadButtons(container, action_dl_vids, action_dl_stories, action_dl_imgs, action_dl_likes)
- ids.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=Consts.DEFAULT_PAD, pady=Consts.DEFAULT_PAD)
- btns.pack(side=tk.RIGHT, expand=False, fill=tk.Y, padx=Consts.DEFAULT_PAD, pady=Consts.DEFAULT_PAD)
- return container
- def CheckboxesAndLimitInputs(parent, var_hq, var_add_date, var_verify, var_convert, var_delete, var_rm_sd_suffix, var_max_vids, var_max_file_len):
- container = tk.Frame(parent)
- checkboxes = Checkboxes(container, var_hq, var_add_date, var_verify, var_convert, var_delete, var_rm_sd_suffix)
- limits = LimitInputs(container, var_max_vids, var_max_file_len)
- checkboxes.pack(side=tk.LEFT, expand=False, fill=tk.X, padx=Consts.DEFAULT_PAD)
- limits.pack(side=tk.RIGHT, expand=True, fill=tk.X, padx=Consts.DEFAULT_PAD)
- return container
- def ConfigInputs(parent, var_sid, var_uid, var_save_dir, var_awemer, var_usr_agent, var_rand_usr_agent):
- container = tk.Frame(parent)
- input_sid = LabeledInput(container, title="sid_tt", textvar=var_sid, hint="Your private sid_tt cookie from \".tiktok.com\"")
- input_uid = LabeledInput(container, title="(You)r uid", textvar=var_uid, hint="Your user ID")
- input_usr_agent = LabeledInputWithCheckbox(container, title="User-Agent", textvar=var_usr_agent, hint="Set a custom User-Agent or use a random one. Leave blank for default.", check_text="Randomize", check_var=var_rand_usr_agent)
- input_save_dir = LabeledInputWithAction(container, title="Save to", textvar=var_save_dir, hint="Save downloads to this location", action_text="Select", action=lambda: var_save_dir.set(askdirectory()))
- input_awemer_dir = LabeledInputWithAction(container, title="videos-node.js", textvar=var_awemer, hint="Location of the videos-node.js file", action_text="Select", action=lambda: var_awemer.set(askopenfilename(filetypes=[("videos-node.js", "*.js")])))
- input_sid.pack(side=tk.TOP, expand=True, fill=tk.X, padx=Consts.DEFAULT_PAD, pady=Consts.DEFAULT_PAD)
- input_uid.pack(side=tk.TOP, expand=True, fill=tk.X, padx=Consts.DEFAULT_PAD, pady=Consts.DEFAULT_PAD)
- input_usr_agent.pack(side=tk.TOP, expand=True, fill=tk.X, padx=Consts.DEFAULT_PAD, pady=Consts.DEFAULT_PAD)
- input_save_dir.pack(side=tk.TOP, expand=True, fill=tk.X, padx=Consts.DEFAULT_PAD, pady=Consts.DEFAULT_PAD)
- input_awemer_dir.pack(side=tk.TOP, expand=True, fill=tk.X, padx=Consts.DEFAULT_PAD, pady=Consts.DEFAULT_PAD)
- return container
- ##### Sub-window parts #####
- def IdsInput(parent, var_ids):
- container = tk.Frame(parent)
- label = tk.Label(container, text="IDs", width=18, anchor=tk.W, justify=tk.LEFT, font=(None, Consts.LABEL_SIZE))
- id_field = tk.Text(container, width=28, height=10)
- id_field.bind('<KeyRelease>', lambda *args: var_ids.set(id_field.get("1.0", tk.END)[:-1])) # get seems to \n terminate the string additionally
- var_ids.trace_add("write", lambda *args: set_text_widget(id_field, var_ids.get()))
- CreateToolTip(id_field, "IDs or @name of a user, IDs of a music, IDs or #tag of a hashtag, IDs of a video.\nIf this is blank (You)r UID will be used for the user parameter instead.")
- label.pack(side=tk.TOP, fill=tk.X, expand=False)
- id_field.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
- return container
- def DownloadButtons(parent, action_dl_vids, action_dl_stories, action_dl_imgs, action_dl_likes):
- container = tk.Frame(parent)
- label = tk.Label(container, text="Download", width=18, anchor=tk.W, justify=tk.LEFT, font=(None, Consts.LABEL_SIZE))
- dl_videos = tk.Button(container, text="Videos", height=2, command=action_dl_vids)
- dl_stories = tk.Button(container, text="Stories", height=2, command=action_dl_stories)
- dl_images = tk.Button(container, text="Images", height=2, command=action_dl_imgs)
- dl_likes = tk.Button(container, text="Likes", height=2, command=action_dl_likes)
- label.pack(side=tk.TOP, fill=tk.X, expand=False)
- dl_videos.pack(side=tk.TOP, fill=tk.X, expand=False, pady=Consts.DEFAULT_PAD)
- dl_stories.pack(side=tk.TOP, fill=tk.X, expand=False, pady=Consts.DEFAULT_PAD)
- dl_images.pack(side=tk.TOP, fill=tk.X, expand=False, pady=Consts.DEFAULT_PAD)
- dl_likes.pack(side=tk.TOP, fill=tk.X, expand=False, pady=Consts.DEFAULT_PAD)
- return container
- def Checkboxes(parent, var_hq, var_add_date, var_verify, var_convert, var_delete, var_rm_sd_suffix):
- container = tk.Frame(parent)
- opt_hq = tk.Checkbutton(container, text='HD', variable=var_hq)
- opt_add_date = tk.Checkbutton(container, text='Date in filename', variable=var_add_date)
- opt_verify = tk.Checkbutton(container, text='Verify', variable=var_verify)
- opt_convert = tk.Checkbutton(container, text='Convert HEVC', variable=var_convert)
- opt_delete = tk.Checkbutton(container, text='Delete HEVC', variable=var_delete)
- opt_rm_sd_suffix = tk.Checkbutton(container, text='Rename SD', variable=var_rm_sd_suffix)
- CreateToolTip(opt_hq, "Download higher quality videos (downloads standard quality if HD fails)")
- CreateToolTip(opt_add_date, "Add the timestamp to the saved video filename")
- CreateToolTip(opt_verify, "[Requires ffprobe]\nDownload videos with a ~verify suffix, pass them through ffprobe after downloading to check their integrity, and rename with an ~error suffix if it fails")
- CreateToolTip(opt_convert, "[Requires ffprobe & ffmpeg]\nIf the downloaded video is HEVC then convert it to a H264 version with a ~h264 suffix")
- CreateToolTip(opt_delete, "Clean up downloads after converting - deletes HEVC files and renames H264 files to have no suffix")
- CreateToolTip(opt_rm_sd_suffix, "Clean up downloads - renames SD files to not have the '~sd' suffix")
- toggle_enabled(opt_delete, var_convert.get(), var_delete)
- var_convert.trace("w", lambda *args: toggle_enabled(opt_delete, var_convert.get(), var_delete))
- opt_hq.pack(side=tk.TOP, expand=False, anchor=tk.W)
- opt_add_date.pack(side=tk.TOP, expand=False, anchor=tk.W)
- opt_verify.pack(side=tk.TOP, expand=False, anchor=tk.W)
- opt_convert.pack(side=tk.TOP, expand=False, anchor=tk.W)
- opt_delete.pack(side=tk.TOP, expand=False, anchor=tk.W)
- opt_rm_sd_suffix.pack(side=tk.TOP, expand=False, anchor=tk.W)
- return container
- def LimitInputs(parent, var_max_vids, var_max_file_len):
- container = tk.Frame(parent)
- input_max_vids = LabeledInput(container, title="Max videos", textvar=var_max_vids, hint="Maximum number of items to download.\nAlternatively can be a date how far back to fetch (formatted as yyyy-MM-dd).")
- input_filename_len = LabeledInput(container, title="Description length", textvar=var_max_file_len, hint="Append the video description to the filename.\nLeave empty for no description, 0 for unlimited length or a number to limit the length of the description.")
- input_max_vids.pack(side=tk.TOP, expand=True, fill=tk.X)
- input_filename_len.pack(side=tk.TOP, expand=True, fill=tk.X)
- return container
- ##### UI Utility #####
- class CreateToolTip(object):
- def __init__(self, widget, text='widget info'):
- if Consts.SHOW_TOOLTIPS:
- self.waittime = 500 #miliseconds
- self.wraplength = 180 #pixels
- self.widget = widget
- self.text = text
- self.widget.bind("<Enter>", self.enter)
- self.widget.bind("<Leave>", self.leave)
- self.widget.bind("<ButtonPress>", self.leave)
- self.id = None
- self.tw = None
- def enter(self, event=None):
- self.schedule()
- def leave(self, event=None):
- self.unschedule()
- self.hidetip()
- def schedule(self):
- self.unschedule()
- self.id = self.widget.after(self.waittime, self.showtip)
- def unschedule(self):
- id = self.id
- self.id = None
- if id:
- self.widget.after_cancel(id)
- def showtip(self, event=None):
- x = y = 0
- x, y, cx, cy = self.widget.bbox("insert")
- x += self.widget.winfo_rootx() + 25
- y += self.widget.winfo_rooty() + 20
- # creates a toplevel window
- self.tw = tk.Toplevel(self.widget)
- # Leaves only the label and removes the app window
- self.tw.wm_overrideredirect(True)
- self.tw.wm_geometry("+%d+%d" % (x, y))
- label = tk.Label(self.tw, text=self.text, justify='left', background="#ffffff", relief='solid', borderwidth=1, wraplength = self.wraplength)
- label.pack(ipadx=1)
- def hidetip(self):
- tw = self.tw
- self.tw= None
- if tw:
- tw.destroy()
- def LabeledInput(parent, title, textvar, hint=None):
- container = tk.Frame(parent)
- label = tk.Label(container, text=title, width=18, anchor=tk.W, justify=tk.LEFT, font=(None, Consts.LABEL_SIZE))
- entry = tk.Entry(container, textvariable=textvar)
- if hint != None:
- CreateToolTip(entry, hint)
- label.pack(side=tk.TOP, fill=tk.X, expand=True)
- entry.pack(side=tk.BOTTOM, fill=tk.X, expand=True, padx=2)
- return container
- def LabeledInputWithAction(parent, title, textvar, action_text, action, hint=None):
- container = tk.Frame(parent)
- label = tk.Label(container, text=title, width=18, anchor=tk.W, justify=tk.LEFT, font=(None, Consts.LABEL_SIZE))
- entry = tk.Entry(container, textvariable=textvar)
- button = tk.Button(container, text=action_text, command=action)
- if hint != None:
- CreateToolTip(entry, hint)
- label.pack(side=tk.TOP, fill=tk.X, expand=True)
- entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
- button.pack(side=tk.RIGHT, expand=False, padx=2)
- return container
- def LabeledInputWithCheckbox(parent, title, textvar, check_text, check_var, hint=None):
- container = tk.Frame(parent)
- label = tk.Label(container, text=title, width=18, anchor=tk.W, justify=tk.LEFT, font=(None, Consts.LABEL_SIZE))
- entry = tk.Entry(container, textvariable=textvar)
- check = tk.Checkbutton(container, text=check_text, variable=check_var)
- toggle_enabled(entry, not check_var.get(), None)
- check_var.trace_add("write", lambda *args: toggle_enabled(entry, not check_var.get(), None))
- if hint != None:
- CreateToolTip(entry, hint)
- label.pack(side=tk.TOP, fill=tk.X, expand=True)
- entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
- check.pack(side=tk.RIGHT, expand=False, padx=2)
- return container
- def AlertDialog(text):
- showerror(title="Something went wrong", message=text)
- def toggle_enabled(widget, enabled, var_value):
- if enabled:
- widget["state"] = tk.NORMAL
- else:
- if var_value != None:
- var_value.set(False)
- widget["state"] = tk.DISABLED
- def set_text_widget(widget, text):
- if widget.get("1.0", tk.END)[:-1] != text:
- widget.delete("1.0", tk.END)
- widget.insert("1.0", text)
- ##### Main #####
- def on_closing():
- save_config()
- window.destroy()
- def run_awemer(dl_type):
- executor = AwemerWrapper() \
- .with_ids(var_ids.get()) \
- .with_hq(var_hq.get()) \
- .with_add_date(var_add_date.get()) \
- .with_verify(var_verify.get()) \
- .with_convert(var_convert.get()) \
- .with_delete(var_delete.get()) \
- .with_rm_sd_suffix(var_rm_sd_suffix.get()) \
- .with_sid(var_sid.get()) \
- .with_uid(var_uid.get()) \
- .with_usr_agent(var_usr_agent.get()) \
- .with_random_usr_agent(var_rand_usr_agent.get()) \
- .with_max_results(var_max_vids.get()) \
- .with_max_filename_len(var_max_file_len.get()) \
- .with_save_dir(var_save_dir.get()) \
- .with_awemer(var_awemer.get())
- threading.Thread(target=lambda: executor.execute(dl_type, lambda out: var_statusbar_text.set(out), AlertDialog)).start()
- window = tk.Tk()
- window.title("Awemepy")
- var_ids = tk.StringVar()
- var_max_vids = tk.StringVar()
- var_max_file_len = tk.StringVar()
- var_sid = tk.StringVar()
- var_uid = tk.StringVar()
- var_usr_agent = tk.StringVar()
- var_rand_usr_agent = tk.BooleanVar()
- var_save_dir = tk.StringVar()
- var_awemer = tk.StringVar()
- var_hq = tk.BooleanVar()
- var_add_date = tk.BooleanVar()
- var_verify = tk.BooleanVar()
- var_convert = tk.BooleanVar()
- var_delete = tk.BooleanVar()
- var_rm_sd_suffix = tk.BooleanVar()
- var_statusbar_text = tk.StringVar()
- action_dl_vids = lambda: run_awemer(DownloadType.VIDEOS)
- action_dl_stories = lambda: run_awemer(DownloadType.STORIES)
- action_dl_imgs = lambda: run_awemer(DownloadType.IMAGES)
- action_dl_likes = lambda: run_awemer(DownloadType.LIKES)
- container_top = MainInputAndDownloadBtns(window, var_ids, action_dl_vids, action_dl_stories, action_dl_imgs, action_dl_likes)
- container_middle = CheckboxesAndLimitInputs(window, var_hq, var_add_date, var_verify, var_convert, var_delete, var_rm_sd_suffix, var_max_vids, var_max_file_len)
- container_input_fields = ConfigInputs(window, var_sid, var_uid, var_save_dir, var_awemer, var_usr_agent, var_rand_usr_agent)
- # statusbar inside its own container so that it can have a fixed size and prevent window resizing when status is too long
- container_statusbar = tk.Frame(window)
- statusbar = tk.Label(container_statusbar, bd=1, relief=tk.SUNKEN, anchor=tk.E, bg="lightgray", textvariable=var_statusbar_text)
- container_top.pack(side=tk.TOP, expand=True, fill=tk.BOTH)
- container_middle.pack(side=tk.TOP, expand=False, fill=tk.X)
- container_input_fields.pack(side=tk.TOP, expand=False, fill=tk.X, pady=(0, Consts.DEFAULT_PAD))
- container_statusbar.pack(side=tk.BOTTOM, fill=tk.X)
- statusbar.pack(side=tk.BOTTOM, fill=tk.X, ipadx=2, ipady=2)
- window.update()
- h = window.winfo_height()
- w = window.winfo_width()
- window.minsize(width=w, height=h)
- # prevent statusbar from resizing windown when text is long after the first update
- container_statusbar.pack_propagate(False)
- if Consts.SAVE_CFG_ON_EXIT:
- window.protocol("WM_DELETE_WINDOW", on_closing)
- load_config()
- window.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment