def main(): """jwb-offline Video player script """ def handler(signal, frame): raise KeyboardInterrupt signal.signal(signal.SIGTERM, handler) parser = argparse.ArgumentParser( prog='jwb-offline', usage='%(prog)s [DIR] [COMMAND]', description='Shuffle and play videos in DIR') parser.add_argument('dir', nargs='?', metavar='DIR', default='.') parser.add_argument( 'cmd', nargs='+', metavar='COMMAND', help= 'video player command, "{}" gets replaced by starting position in secs' ) parser.add_argument('--replay-sec', metavar='SEC', type=int, default=30, dest='replay', help='seconds to replay after a restart') parser.add_argument('--verbose', action='store_true', help='show video player output') args = parser.parse_args() args.dir = Path(args.dir) m = VideoManager(args.dir, replay=args.replay, cmd=args.cmd, verbose=args.verbose) try: m.read_dump() except json.JSONDecodeError: pass showmsg = True try: while True: if m.set_random_video(): m.play_video() showmsg = True else: if showmsg: msg('no videos to play yet') showmsg = False time.sleep(10) continue except KeyboardInterrupt: msg('aborted') m.write_dump()
def play_video(self): """Play a video""" self.write_dump() msg('playing: ' + self.video.name) cmd = [arg.replace('{}', str(self.pos)) for arg in self.cmd] + [str(self.video)] self.start_time = time.time() if self.verbose: subprocess.call(cmd) else: subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if self.calculate_pos() == 0: self.errors = self.errors + 1 else: self.errors = 0 if self.errors > 10: raise RuntimeError('video player restarting too quickly') self.add_to_history(self.video) self.video = None
def main(): usage = ''' %(prog)s [options] [DIR] %(prog)s [options] --mode=html|m3u|txt [FILE] %(prog)s [options] --mode=run COMMAND [ARGS]''' p = argparse.ArgumentParser( prog='jwb-index', usage=usage, description='Index or download media from jw.org', argument_default=argparse.SUPPRESS ) # Do not overwrite attributes with None p.add_argument('--append', action='store_true', help='append to file instead of overwriting') p.add_argument('--category', '-c', dest='include_categories', metavar='CODE', action=action_factory(lambda x: x.split(',')), help='comma separated list of categories to index') p.add_argument('--checksum', action='store_true', dest='checksums', help="validate MD5 checksums") p.add_argument('--clean-symlinks', action='store_true', dest='clean_all_symlinks', help='remove all old symlinks (mode=filesystem)') p.add_argument('--download', '-d', action='store_true', help='download media files') p.add_argument('--download-subtitles', action='store_true', help='download VTT subtitle files') p.add_argument( '--exclude', metavar='CODE', dest='exclude_categories', action=action_factory(lambda x: x.split(',')), help= 'comma separated list of categories to skip (sub-categories will also be skipped)' ) p.add_argument( '--fix-broken', action='store_true', dest='overwrite_bad', help='check existing files and re-download them if they are broken') p.add_argument( '--free', type=int, metavar='MiB', dest='keep_free', action=action_factory(lambda x: x * 1024 * 1024), # MiB to B help= 'disk space in MiB to keep free (warning: deletes old MP4 files, use separate folder!)' ) p.add_argument('--friendly', '-H', action='store_true', dest='friendly_filenames', help='save downloads with human readable names') p.add_argument('--hard-subtitles', action='store_true', help='prefer videos with hard-coded subtitles') p.add_argument('--import', dest='import_dir', metavar='DIR', help='import of media files from this directory (offline)') p.add_argument('--lang', '-l', action=action_factory(verify_language), help='language code') p.add_argument('--languages', '-L', nargs=0, action=action_factory(print_language), help='display a list of valid language codes') p.add_argument('--latest', action='store_true', help='index the "Latest Videos" category only') p.add_argument( '--limit-rate', '-R', metavar='RATE', type=float, dest='rate_limit', help= 'maximum download rate, in megabytes/s (default = 1 MB/s, 0 = no limit)' ) p.add_argument('--list-categories', '-C', nargs='?', const='VideoOnDemand', metavar='CODE', dest='print_category', help='print a list of (sub) category names') p.add_argument('--mode', '-m', choices=[ 'filesystem', 'html', 'html_tree', 'm3u', 'm3u_multi', 'm3u_tree', 'run', 'stdout', 'txt' ], help='output mode (see wiki)') p.add_argument('--no-warning', dest='warning', action='store_false', help='do not warn when space limit seems wrong') p.add_argument('--quality', '-Q', type=int, choices=[240, 360, 480, 720], help='maximum video quality') p.add_argument('--quiet', '-q', action='count', help='Less info, can be used multiple times') p.add_argument('--since', metavar='YYYY-MM-DD', dest='min_date', action=action_factory( lambda x: time.mktime(time.strptime(x, '%Y-%m-%d'))), help='only index media newer than this date') p.add_argument('--sort', choices=['newest', 'oldest', 'name', 'random'], help='sort output') p.add_argument( '--update', action='store_true', help= 'update existing categories with the latest videos (implies --append --latest --sort=newest)' ) p.add_argument('positional_arguments', nargs='*', metavar='DIR|FILE|COMMAND', help='where to send output (depends on mode)') s = p.parse_args(namespace=Settings()) # Quick print of categories list if s.print_category: print(*get_categories(s, s.print_category), sep='\n') exit() # Required arguments if not (s.mode or s.download or s.download_subtitles or s.import_dir): msg('please use --mode or --download') exit(1) # Implicit arguments if s.update: s.append = True s.latest = True if not s.sort: s.sort = 'newest' if s.latest: for key in s.include_categories: if key != 'VideoOnDemand': # Add key and its sub categories to the filter s.filter_categories.append(key) if s.quiet < 1: msg('preparing filter: ' + key) s.filter_categories += get_categories(s, key) s.include_categories = ['LatestVideos'] # Handle positional arguments depending on mode # COMMAND [ARGS] if s.mode == 'run': if not s.positional_arguments: msg('--mode=run requires a command') exit(1) s.command = s.positional_arguments elif len(s.positional_arguments) == 1: path = Path(s.positional_arguments[0]) # FILE if s.mode in ('txt', 'm3u', 'html') and not path.is_dir(): s.output_filename = path.name s.work_dir = path.parent # DIR else: s.work_dir = path elif len(s.positional_arguments) > 1: msg('unexpected argument: {}'.format(s.positional_arguments[1])) exit(1) # Check / set paths if not s.work_dir.is_dir(): msg('not a directory: ' + str(s.work_dir)) exit(1) if s.mode not in ('', 'stdout'): s.sub_dir = 'jwb-' + s.lang # Warning if disk space is already below limit if (s.download or s.import_dir) and s.keep_free > 0: disk_usage_info(s) # Offline import (stops here) if s.import_dir: copy_files(s) exit() # NTFS compatibility (try to create a forbidden file) try: (s.work_dir / '?').touch(exist_ok=False) (s.work_dir / '?').unlink() except FileExistsError: pass except OSError: s.safe_filenames = True # Some heads-up if s.quiet < 1: if s.download and s.rate_limit: msg('note: download rate limit is active') if s.safe_filenames: msg('note: using NTFS/FAT compatible file names') # Do the indexing data = parse_broadcasting(s) if s.download or s.download_subtitles: download_all(s, data) if s.mode: create_output(s, data)
def print_language(x): msg('language codes:') for l in get_jwb_languages(): msg('{:>3} {:<}'.format(l['code'], l['name'])) exit()