1
0
mirror of https://github.com/spaam/svtplay-dl.git synced 2024-11-24 12:15:40 +01:00
svtplay-dl/lib/svtplay_dl/output.py
Olof Johansson 44293899fc Don't workaround windows cmdline parsing brokeness
We should be able to assume that if we see literal " in the name of the
output file, the user really wants the quotes. Assuming otherwise opens a
can of worm. What else can't we trust in the environment?

On the Windows command line, \ has special meaning in strings, and it's not
the same as on normal platforms: it only has special meaning if it's
immediately followed by a " character, in which it case it will make the "
character be interepreted literally. That's what we've been seeing.

Incorrect command line on windows:

     python svtplay-dl -o "C:\foo\"

Correct command line on windows:

     python svtplay-dl -o "C:\foo\\"

This has been made harder to pinpoint because of the forgiving nature of
command line parsing on windows. For instance: The following command was
reported to work:

     python svtplay-dl http://example.com/ -o "C:\foo\"

But only if the -o flag was the last argument. (Unclear if the trailing "
was passed on to the program or not.)

Reference: https://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft(v=vs.85).aspx
2015-10-25 19:41:39 +01:00

200 lines
6.2 KiB
Python

# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
from __future__ import absolute_import
import sys
import time
import re
import os
import io
import platform
from datetime import timedelta
from svtplay_dl.utils import is_py3, is_py2, filenamify, decode_html_entities, ensure_unicode
from svtplay_dl.utils.terminal import get_terminal_size
from svtplay_dl.log import log
progress_stream = sys.stderr
class ETA(object):
"""
An ETA class, used to calculate how long it takes to process
an arbitrary set of items. By initiating the object with the
number of items and continuously updating with current
progress, the class can calculate an estimation of how long
time remains.
"""
def __init__(self, end, start=0):
"""
Parameters:
end: the end (or size, of start is 0)
start: the starting position, defaults to 0
"""
self.start = start
self.end = end
self.pos = start
self.now = time.time()
self.start_time = self.now
def update(self, pos):
"""
Set new absolute progress position.
Parameters:
pos: new absolute progress
"""
self.pos = pos
self.now = time.time()
def increment(self, skip=1):
"""
Like update, but set new pos relative to old pos.
Parameters:
skip: progress since last update (defaults to 1)
"""
self.update(self.pos + skip)
@property
def left(self):
"""
returns: How many item remains?
"""
return self.end - self.pos
def __str__(self):
"""
returns: a time string of the format HH:MM:SS.
"""
duration = self.now - self.start_time
# Calculate how long it takes to process one item
try:
elm_time = duration / (self.end - self.left)
except ZeroDivisionError:
return "(unknown)"
return str(timedelta(seconds=int(elm_time * self.left)))
def progress(byte, total, extra=""):
""" Print some info about how much we have downloaded """
if total == 0:
progresstr = "Downloaded %dkB bytes" % (byte >> 10)
progress_stream.write(progresstr + '\r')
return
progressbar(total, byte, extra)
def progressbar(total, pos, msg=""):
"""
Given a total and a progress position, output a progress bar
to stderr. It is important to not output anything else while
using this, as it relies soley on the behavior of carriage
return (\\r).
Can also take an optioal message to add after the
progressbar. It must not contain newlines.
The progress bar will look something like this:
[099/500][=========...............................] ETA: 13:36:59
Of course, the ETA part should be supplied be the calling
function.
"""
width = get_terminal_size()[0] - 35
rel_pos = int(float(pos)/total*width)
bar = ''.join(["=" * rel_pos, "." * (width - rel_pos)])
# Determine how many digits in total (base 10)
digits_total = len(str(total))
fmt_width = "%0" + str(digits_total) + "d"
fmt = "\r[" + fmt_width + "/" + fmt_width + "][%s] %s"
progress_stream.write(fmt % (pos, total, bar, msg))
def filename(options, stream):
if options.output:
if is_py2:
if platform.system() == "Windows":
options.output = options.output.decode("latin1")
else:
options.output = options.output.decode("utf-8")
if not options.output or os.path.isdir(options.output):
data = ensure_unicode(stream.get_urldata())
if data is None:
return False
match = re.search(r"(?i)<title[^>]*>\s*(.*?)\s*</title>", data, re.S)
if match:
options.output_auto = True
title_tag = decode_html_entities(match.group(1))
if not options.output:
options.output = filenamify(title_tag)
else:
# output is a directory
options.output = os.path.join(options.output, filenamify(title_tag))
return True
def output(options, extention="mp4", openfd=True, mode="wb", **kwargs):
if is_py3:
file_d = io.IOBase
else:
file_d = file
if options.output != "-":
ext = re.search(r"(\.\w{2,3})$", options.output)
if not ext:
options.output = "%s.%s" % (options.output, extention)
if options.output_auto and ext:
options.output = "%s.%s" % (options.output, extention)
if extention == "srt" and ext:
options.output = "%s.srt" % options.output[:options.output.rfind(ext.group(1))]
log.info("Outfile: %s", options.output)
if os.path.isfile(options.output) or \
findexpisode(os.path.dirname(os.path.realpath(options.output)), options.service, os.path.basename(options.output)):
if extention == "srt":
if not options.force_subtitle:
log.error("File already exists. Use --force-subtitle to overwrite")
return None
else:
if not options.force:
log.error("File already exists. Use --force to overwrite")
return None
if openfd:
file_d = open(options.output, mode, **kwargs)
else:
if openfd:
if is_py3:
file_d = sys.stdout.buffer
else:
file_d = sys.stdout
return file_d
def findexpisode(directory, service, name):
match = re.search(r"-(\w+)-\w+.(\w{2,3})$", name)
if not match:
return False
videoid = match.group(1)
extention = match.group(2)
files = [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]
for i in files:
match = re.search(r"-(\w+)-\w+.(\w{2,3})$", i)
if match:
if service:
if extention == "srt":
if name.find(service) and match.group(1) == videoid and match.group(2) == extention:
return True
elif match.group(2) != "srt":
if name.find(service) and match.group(1) == videoid:
return True
return False