Index: contrib/recordflags.py ================================================================== --- contrib/recordflags.py +++ contrib/recordflags.py @@ -1,10 +1,10 @@ # encoding: utf-8 # api: streamtuner2 # title: Recording options # description: Allows to set streamripper/fIcy options before recording -# version: 0.8.5 +# version: 0.9 # depends: streamtuner2 > 2.2.0 # conflicts: continuous_record # priority: optional # config: # { name: recordflags_auto, type: bool, value: 1, description: Apply options automatically once saved. } @@ -58,32 +58,35 @@ "category": "recording", "version": "1.64.6", "description": "Standard radio/stream recording tool", "doc": "streamripper is the standard tool for recording and extracting songs from internet radio stations. It does have a plethora of options, some of which are available here:", "config": [ - #{ "name": "A", "arg": "-A", "type": "bool", "description": "-A Don't split individual tracks/MP3s", "value": False }, + # basic { "name": "Aa", "arg": "-A -a", "type": "str", "description": "-a Single MP3 output filename. (Instead of splitting by song.)", "value": "" }, { "name": "dir", "arg": "-d", "type": "str", "description": "-d Destination directory", "value": "" }, - { "name": "D", "arg": "-D", "type": "str", "description": "-D Filename pattern", "value": "" }, { "name": "s", "arg": "-s", "type": "bool", "description": "-s No subdirectories for each stream", "value": False }, + { "name": "D", "arg": "-D", "type": "str", "description": "-D Filename pattern", "value": "" }, { "name": "o", "arg": "-o", "type": "select", "description": "-o Incomplete track overwriting", "select": "|always|never|larger|version", "value": "" }, - { "name": "t", "arg": "-t", "type": "bool", "description": "-t Never overwrite incomplete tracks", "value": False, "category": "extra" }, - { "name": "T", "arg": "-T", "type": "bool", "description": "-T Truncate duplicated incompletes", "value": False, "category": "extra" }, { "name": "l", "arg": "-l", "type": "int", "description": "-l Seconds to record", "value": 0, "max": 7*24*3600 }, { "name": "M", "arg": "-M", "type": "int", "description": "-M Max megabytes to record", "value": 512 }, { "name": "xs2", "arg": "--xs2", "type": "bool", "description": "--xs2 New pause detection algorithm", "value": False }, - { "name": "xsnone", "arg": "--xs-none", "type": "bool", "description": "--xs-none No silence splitting", "value": False, "category": "extra" }, - { "name": "i", "arg": "-i", "type": "bool", "description": "-i Don't add any ID3 tags", "value": False, "category": "extra" }, - { "name": "id3v1", "arg": "--with-id3v1", "type": "bool", "description": "--with-id3v1 Add ID3v1 tags", "value": False, "category": "extra" }, - { "name": "noid3v2", "arg": "--without-id3v2", "type": "bool", "description": "--without-id3v2 Omit ID3v2 tags", "value": False, "category": "verbose" }, - { "name": "cs_fs", "arg": "--codeset-filesys", "type": "str", "description": "Charset filesystem", "value": "", "category": "extra" }, - { "name": "cs_id3", "arg": "--codeset-id3", "type": "str", "description": "Charset ID3 tags", "value": "", "category": "extra" }, - { "name": "u", "arg": "-u", "type": "str", "description": "-u User-agent (browser id)", "value": "", "category": "extra" }, - { "name": "p", "arg": "-p", "type": "str", "description": "-p Url for HTTP proxy to use", "value": "", "category": "extra" }, - { "name": "r", "arg": "-r", "type": "str", "description": "-r Relay server 'localhost:8000'", "value": "", "category": "extra" }, - { "name": "m", "arg": "-m", "type": "int", "description": "-mTimeout for stalled connection", "value": 15, "category": "verbose" }, - { "name": "debug", "arg": "--debug", "type": "bool", "description": "--debug Extra verbosity", "value": False, "category": "verbose"}, + { "name": "xsnone", "arg": "--xs-none", "type": "bool", "description": "--xs-none No silence splitting", "value": False }, + # meta + { "name": "A", "arg": "-A", "type": "bool", "description": "-A Don't split individual tracks/MP3s", "value": False, "category": "meta" }, + { "name": "i", "arg": "-i", "type": "bool", "description": "-i Don't add any ID3 tags", "value": False, "category": "meta" }, + { "name": "noid3v2", "arg": "--without-id3v2", "type": "bool", "description": "--without-id3v2 Omit ID3v2 tags", "value": False, "category": "meta" }, + { "name": "id3v1", "arg": "--with-id3v1", "type": "bool", "description": "--with-id3v1 Add ID3v1 tags", "value": False, "category": "meta" }, + { "name": "cs_fs", "arg": "--codeset-filesys", "type": "str", "description": "Charset filesystem", "value": "", "category": "meta" }, + { "name": "cs_id3", "arg": "--codeset-id3", "type": "str", "description": "Charset ID3 tags", "value": "", "category": "meta" }, + { "name": "t", "arg": "-t", "type": "bool", "description": "-t Never overwrite incomplete tracks", "value": False, "category": "meta" }, + { "name": "T", "arg": "-T", "type": "bool", "description": "-T Truncate duplicated incompletes", "value": False, "category": "meta" }, + # net + { "name": "p", "arg": "-p", "type": "str", "description": "-p Url for HTTP proxy to use", "value": "", "category": "net" }, + { "name": "r", "arg": "-r", "type": "str", "description": "-r Relay server 'localhost:8000'", "value": "", "category": "net" }, + { "name": "u", "arg": "-u", "type": "str", "description": "-u User-agent (browser id)", "value": "", "category": "net" }, + { "name": "m", "arg": "-m", "type": "int", "description": "-m Timeout for stalled connection", "value": 15, "category": "net" }, + { "name": "debug", "arg": "--debug", "type": "bool", "description": "--debug Extra verbosity", "value": False, "category": "net"}, ] }, "fPls": { "title": "fPls/fIcy", "priority": "required", @@ -90,20 +93,23 @@ "type": "app", "category": "recording", "version": "1.0.19", "description": "Alternative station recording tool", "config": [ + # basic { "name": "max", "arg": "-M", "type": "int", "description": "-M Maximum cumulative playing time", "value": 0 }, { "name": "loop", "arg": "-L", "type": "int", "description": "-L Maximum playlist loops", "value": 0 }, { "name": "retry", "arg": "-R", "type": "int", "description": "-R Maximum per-stream retries", "value": 0 }, { "name": "redir", "arg": "-l", "type": "int", "description": "-l Redirect follow limit", "value": 0 }, { "name": "fail", "arg": "-T", "type": "int", "description": "-T Wait time after failure", "value": 0 }, - { "name": "daemon", "arg": "-i", "type": "int", "description": "-i Max network idle seconds", "value": 0 }, - { "name": "authfn", "arg": "-a", "type": "str", "description": "-a HTTP auth file (user:pass)", "value": "", "category": "extra" }, - { "name": "verbose", "arg": "-v", "type": "bool", "description": "-v Verbose mode", "value": False, "category": "verbose" }, - { "name": "daemon", "arg": "-d", "type": "str", "description": "-d Daemon mode: log file", "value": "", "category": "verbose" }, - { "name": "ficy", "arg": "-P", "type": "str", "description": "-P Path to fIcy", "value": "", "category": "extra" }, + # meta + { "name": "ficy", "arg": "-P", "type": "str", "description": "-P Path to fIcy", "value": "", "category": "meta" }, + # net + { "name": "daemon", "arg": "-i", "type": "int", "description": "-i Max network idle seconds", "value": 0, "category": "net" }, + { "name": "authfn", "arg": "-a", "type": "str", "description": "-a HTTP auth file (user:pass)", "value": "", "category": "net" }, + { "name": "verbose", "arg": "-v", "type": "bool", "description": "-v Verbose mode", "value": False, "category": "net" }, + { "name": "daemon", "arg": "-d", "type": "str", "description": "-d Daemon mode: log file", "value": "", "category": "net" }, ] }, "youtube-dl": { "title": "youtuble-dl", "priority": "required", @@ -110,19 +116,41 @@ "type": "app", "category": "download", "version": "2017.02.11.3", "description": "Youtube downloader", "config": [ + # basic { "name": "freeformats", "arg": "--prefer-free-formats", "type": "bool", "description": "Prefer free audio formats", "value": False }, { "name": "format", "arg": "-f", "type": "select", "select": "=any|b=best|249=webm audio only DASH|250=webm audio only DASH|140=m4a audio only DASH|171=webm audio only DASH|251=webm audio only DASH|278=webm 256x144 DASH|160=mp4 256x144 DASH|242=webm 426x240 DASH|133=mp4 426x240 DASH|243=webm 640x360 DASH|134=mp4 640x360 DASH|244=webm 854x480 DASH|135=mp4 854x480 DASH|247=webm 1280x720 DASH|136=mp4 1280x720 DASH|248=webm 1920x1080 DASH|137=mp4 1920x1080 DASH|17=3gp 176x144 small|36=3gp 320x180 small|43=webm 640x360 medium|18=mp4 640x360 medium|22=mp4 1280x720 hd720", "description": "-f Format", "value": "b" }, - { "name": "c", "arg": "-c", "type": "bool", "description": "-c Continue partial downloads ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ", "value": True }, - { "name": "netrc", "arg": "-n", "type": "bool", "description": "-n Use .netrc for auth/login", "value": False, "category": "extra" }, + { "name": "c", "arg": "-c", "type": "bool", "description": "-c Continue partial downloads", "value": True }, + { "name": "o", "arg": "-o", "type": "str", "description": "-o Output TEMPLATE", "value": "" }, + { "name": "ascii", "arg": "--restrict-filenames", "type": "bool", "description": "--restrict-filenames to ASCII", "value": False }, + { "name": "id", "arg": "--id", "type": "bool", "description": "Use only --id in filename", "value": False }, + { "name": "xa", "arg": "-x", "type": "bool", "description": "-x Extract audio only", "value": False }, + { "name": "recode", "arg": "--recode-format", "type": "select", "select": "|mp4|flv|ogg|webm|mkv|avi", "description": "--recode-format", "value": "" }, + # meta + { "name": "w_desc", "arg": "--write-description", "type": "bool", "description": "--write-description file", "value": False, "category": "meta" }, + { "name": "w_json", "arg": "--write-info-json", "type": "bool", "description": "--write-info-json file", "value": False, "category": "meta" }, + { "name": "w_anno", "arg": "--write-annotations", "type": "bool", "description": "--write-annotations xml", "value": False, "category": "meta" }, + { "name": "e_subs", "arg": "--embed-subs", "type": "bool", "description": "--embed-subs in video files", "value": False, "category": "meta" }, + { "name": "e_thumb", "arg": "--embed-thumbnail", "type": "bool", "description": "--embed-thumbnail as cover", "value": False, "category": "meta" }, + { "name": "add_meta", "arg": "--add-metadata", "type": "bool", "description": "--add-metadata in output file", "value": False, "category": "meta" }, + { "name": "xattrs", "arg": "--xattrs", "type": "bool", "description": "--xattrs for meta data", "value": False, "category": "meta" }, + ##{ "name": "", "arg": "", "type": "bool", "description": "", "value": False, "category": "meta" }, + { "name": "verbose", "arg": "-v", "type": "bool", "description": "-v Verbose mode", "value": False, "category": "meta" }, + { "name": "igncfg", "arg": "--ignore-config", "type": "bool", "description": "--ignore-config", "value": False, "category": "meta" }, + { "name": "downads", "arg": "--download-ads", "type": "bool", "description": "--download-ads", "value": False, "category": "meta" }, + # net + { "name": "ua", "arg": "--user-agent", "type": "str", "description": "--user-agent", "value": "", "category": "net" }, + { "name": "netrc", "arg": "-n", "type": "bool", "description": "-n Use .netrc for auth/login", "value": False, "category": "net" }, + { "name": "proxy", "arg": "--proxy", "type": "str", "description": "-p Proxy", "value": "", "category": "net" }, + { "name": "geoproxy", "arg": "--geo-verification-proxy", "type": "str", "description": "Geo-verification Proxy", "value": "", "category": "net" }, { "name": "ignore", "arg": "-i", "type": "bool", "description": "-i Ignore errors", "value": False }, - { "name": "proxy", "arg": "--proxy", "type": "str", "description": "-p Proxy", "value": "", "category": "extra" }, - { "name": "verbose", "arg": "-v", "type": "bool", "description": "-v Verbose mode", "value": False, "category": "verbose" }, - { "name": "ipv4", "arg": "-4", "type": "bool", "description": "-4 Use IPv4", "value": False, "category": "extra" }, - { "name": "ipv6", "arg": "-6", "type": "bool", "description": "-6 Use IPv6", "value": False, "category": "extra" }, + { "name": "bin", "arg": "--external-downloader", "type": "str", "description": "--external-downloader tool", "value": "", "category": "net" }, + { "name": "ipv4", "arg": "-4", "type": "bool", "description": "-4 Use IPv4", "value": False, "category": "net" }, + { "name": "ipv6", "arg": "-6", "type": "bool", "description": "-6 Use IPv6", "value": False, "category": "net" }, + { "name": "update", "arg": "-U", "type": "bool", "description": "-U Update", "value": False, "category": "net" }, ] }, "wget": { "title": "wget", "priority": "required", @@ -129,17 +157,28 @@ "type": "app", "category": "download", "version": "1.15", "description": "HTTP download utility", "config": [ + # basic { "name": "c", "arg": "-c", "type": "bool", "description": "-c Continue partial downloads.", "value": True }, { "name":"nc", "arg":"-nc", "type": "bool", "description": "-nc No-clobber, keep existing files.", "value": False }, { "name": "N", "arg": "-N", "type": "bool", "description": "-N Only fetch newer files", "value": False }, { "name": "O", "arg": "-O", "type": "str", "description": "-O Output to file", "value": "" }, - { "name": "v", "arg": "-v", "type": "bool", "description": "-v Verbose mode", "value": False, "category": "verbose" }, - { "name": "S", "arg": "-S", "type": "bool", "description": "-S Show response headers", "value": False, "category": "verbose" }, - { "name": "U", "arg": "-U", "type": "str", "description": "-U Useragent to send", "value": "", "category": "extra" }, + { "name": "dir", "arg": "-P", "type": "str", "description": "-P Directory prefix", "value": "" }, + # meta + { "name": "v", "arg": "-v", "type": "bool", "description": "-v Verbose mode", "value": False, "category": "meta" }, + { "name": "d", "arg": "-d", "type": "bool", "description": "-d Debug mode", "value": False, "category": "meta" }, + { "name": "enc", "arg": "--local-encoding", "type": "select", "select": "UTF-8|ISO-8859-1|ISO-8859-15", "description": "--local-encoding", "value": False, "category": "meta" }, + { "name": "e", "arg": "-e", "type": "str", "description": "-e wgetrc-command", "value": "", "category": "meta" }, + # net + { "name": "noch", "arg": "--no-cache", "type": "bool", "description": "-S No cached files", "value": False, "category": "net" }, + { "name": "limit", "arg": "--limit-rate", "type": "int", "description": "--limit-rate Max download speed", "value": 0, "category": "net" }, + { "name": "S", "arg": "-S", "type": "bool", "description": "-S Show response headers", "value": False, "category": "net" }, + { "name": "U", "arg": "-U", "type": "str", "description": "-U Useragent to send", "value": "", "category": "net" }, + { "name": "ref", "arg": "--referer", "type": "str", "description": "--referer to send", "value": "", "category": "net" }, + { "name": "4", "arg": "-4", "type": "bool", "description": "-4 Use IPv4 only", "value": "", "category": "net" }, ] }, } # current selection (dialog only runs once anyway, so we can keep flags in same object) @@ -146,58 +185,53 @@ app = "streamripper" argmap = {} # "--xs2" => "xs2" namemap = {} # "xs2" => "--xs2" typemap = {} # "xs2" => "bool" defmap = {} # "opt" => "default" + catalias = {"verbose": "net", "extra": "meta", None: "basic"} # alias option category: # parameters from current action.record() call k = [] # avoids having to pass them around kw = {} # simplifies gtk callbacks row = {} # only one active instance anyway # hooks for user interface/handlers def init2(self, parent, *k, **kw): - # TEMPORARY WORKAROUND: swap action.record() - action.record = self.action_hook - # BETTER APPROACH: hook record button - #parent.on_record_clicked = self.show_window # default widget actions parent.win_recordoptions.connect("delete-event", self.hide_dialog) parent.recordoptions_go.connect("clicked", self.do_record) parent.recordoptions_save.connect("clicked", self.save_only) - parent.recordoptions_eventbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color("#442211")) + parent.recordoptions_eventbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0x44, 0x22, 0x11)) # shortcuts self.add_plg = parent.configwin.add_plg # create _cfg widgets self.load_config = parent.configwin.load_config # populate _cfg widgets self.save_config = parent.configwin.save_config # save from _cfg widgets self.cfg_vbox = { "basic": self.parent.recordoptions_cfg, - "extra": self.parent.recordoptions_cfg_extra, - "verbose": self.parent.recordoptions_cfg_verbose, + "meta": self.parent.recordoptions_cfg_extra, + "net": self.parent.recordoptions_cfg_verbose, } - # add menu entry (for simple triv#1 option) - #uikit.add_menu([parent.extensions_context], "Set single MP3 record -A flag", self.set_cont) - + # swap out action.record() + action.record = self.action_hook + # The better option would be overriding main.on_record_clicked. + # Though that would require to also adapt GenericChannel.record. + # And would make injecting the append= arguments a bit harder. # prepares a few shortcuts def map_app_args(self, app): config = self.flag_meta[app]["config"] self.argmap = { row["arg"].split(" ")[0]: row["name"] for row in config if row.get("arg") } self.namemap = dict(zip(self.argmap.values(), self.argmap.keys())) self.typemap = { row["name"]: row["type"] for row in config if row.get("type") } self.defmap = { row["name"]: row["value"] for row in config if row.get("value") is not None } - log.CONF(self.defmap) + #log.CONF(self.defmap) - # triv #1 menu option → only saves `-A` flag to row["recordflags"] - #def set_cont(self, row): - # row[conf.recordflags_row] = "-A" - # override GtkWindow.destroy/delete-event def hide_dialog(self, *x): self.parent.win_recordoptions.hide() return True @@ -216,20 +250,21 @@ self.k = k # stash away args: audioformat, source, assoc, append self.kw = kw self.row = row self.show_dialog(self.row) - # only handle audio/* streamripper formats + # check row for matching audio/* MIME, + # and if tool options are configured for it def can_handle(self, row): # search for configured (flag_meta) apps in conf.record["audio/*"] dict rx_apps = "\\b(?i)(" + ("|".join(self.flag_meta.keys())) + ")\\b" cmd = action.mime_app(row.get("format", "audio/*"), conf.record) match = re.findall(rx_apps, cmd or "") - log.PROC(cmd) + #log.PROC(cmd) # if both mime matched, and cmd in supported apps: if cmd and match: - log.STAT(match) + #log.STAT(match) self.app = match[0] self.map_app_args(self.app) return True return False @@ -239,12 +274,13 @@ self.parent.channel().save() # overriden handler, chains to actual recording, invoked by [record] button def do_record(self, *x): self.kw["append"] = self.args_from_configwin().strip() + self.hide_dialog() action.run_fmt_url(self.row, *self.k, **self.kw) - self.hide() + # option window def show_dialog(self, row): p = self.parent # set labels, connect buttons @@ -252,11 +288,10 @@ p.recordoptions_url.set_text(row["url"][0:50]) # add option widgets self.load_config_widgets(row, self.app, p) # show window p.win_recordoptions.show() - # populate config widgets, seth defaults/current settings def load_config_widgets(self, row, group="streamripper", p=None): # clean up previous [vbox.remove(w) for vbox in self.cfg_vbox.values() for w in vbox.get_children()] @@ -265,22 +300,23 @@ # set values self.load_config(self.configdict_from_args(row), self.cfg_widget_pfx, widgets=self.widgets) # Put config widgets into recordoptions_cfg_*** vbox def pack_option(self, id=None, w=None, label=None, color=None, image=None, align=5, opt={}): - vbox = self.cfg_vbox.get(opt.get("category"), self.cfg_vbox["basic"]) - vbox.pack_start(uikit.wrap(self.widgets, id, w, label, color, image, align, label_markup=1, label_size=250)) + category = opt.get("category") + vbox = self.cfg_vbox.get(self.catalias.get(category) or category) or self.cfg_vbox["basic"] + vbox.pack_start(uikit.wrap(self.widgets, id, w, label, color, image, align, label_markup=1, label_size=250), expand=False, fill=False) # return "--args str" for current config widget states def args_from_configwin(self): cfg = { name: None for name in self.namemap.keys() } self.save_config(cfg, self.cfg_widget_pfx, widgets=self.widgets) - log.DATA(cfg) + #log.DATA(cfg) return self.args_from_configdict(cfg) - #-- parse existing `record_flags` and conf.record defauls into name-config + #-- extract saved row[record_flags] and conf.record[] defaults into name-config{} def configdict_from_args(self, row): r = copy.copy(self.defmap) # saved `record_flags` cmdstr = row.get(conf.recordflags_row, "") # add default options from configured recoding app Index: gtk3.xml.gz ================================================================== --- gtk3.xml.gz +++ gtk3.xml.gz cannot compute difference between binary files