Commit 5fd3815e authored by Lysander Trischler's avatar Lysander Trischler

Replace tabs by spaces

parent e2bb1e38
......@@ -40,18 +40,18 @@ import urllib.request
me = os.path.basename(__file__)
class Config(configparser.ConfigParser):
def __init__(self, fp):
configparser.ConfigParser.__init__(self)
self.read([fp])
def __init__(self, fp):
configparser.ConfigParser.__init__(self)
self.read([fp])
def get(self, section, field, default):
try:
return configparser.ConfigParser.get(self, section, field)
except (configparser.NoSectionError, configparser.NoOptionError):
return default
def get(self, section, field, default):
try:
return configparser.ConfigParser.get(self, section, field)
except (configparser.NoSectionError, configparser.NoOptionError):
return default
def path(*path):
return os.path.join(*[os.path.expanduser(os.path.expandvars(segment)) for segment in path])
return os.path.join(*[os.path.expanduser(os.path.expandvars(segment)) for segment in path])
DATA_DIR = path(os.getenv("XDG_DATA_HOME", "~/.local/share"), "yfav")
CONFIG_FILE = path(os.getenv("XDG_CONFIG_HOME", "~/.config"), "yfav")
......@@ -64,171 +64,171 @@ VIDEO_ID_LEN = 11
VIDEO_ID_URL = "http://youtube.com/watch?v="
def now():
return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
import re
# stolen from tornado.escape
_XHTML_ESCAPE_RE = re.compile('[&<>"]')
_XHTML_ESCAPE_DICT = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;'}
def esc(value):
"""Escapes a string so it is valid within XML or XHTML."""
if value is None: return ''
return _XHTML_ESCAPE_RE.sub(lambda match: _XHTML_ESCAPE_DICT[match.group(0)], value)
"""Escapes a string so it is valid within XML or XHTML."""
if value is None: return ''
return _XHTML_ESCAPE_RE.sub(lambda match: _XHTML_ESCAPE_DICT[match.group(0)], value)
class Video(object):
def __init__(self, line=None, id=None, title=None, updated=None, author=None):
if line is not None:
self.id, self.title, self.updated, self.author = line.rstrip().split("\t", 3)
else:
self.id = Video.extract_video_id(id)
self.title = title
self.updated = updated
self.author = author
if not self.id:
raise ValueError("No video ID given!")
if not self.updated:
# It's a pitty the YouTube API does not return video's uplaod timestamps,
# so use current date and time.
self.updated = now()
if not self.title or not self.author:
def oneof(self, *keys):
for key in keys:
val = self.get(key, None)
if val:
return val[0]
return None
# three tries to get the information successfully from the API
for i in range(2):
info = Video.get_info(self.id)
if not self.title:
# sometimes the keys look like a broken Python 2
# byte string literal
self.title = oneof(info, 'title', "b'title")
if not self.author:
self.author = oneof(info, 'author', "b'author")
# refetch data from API if sth. went wrong
if self.title and self.author:
break
sys.stderr.write("%s:\n%r" % (me, info))
else:
sys.stderr.write("%s: Cannot fetch metadata from YT API, tried 3 times!\n" % me)
@classmethod
def get_info(cls, video_id):
info = urllib.request.urlopen('http://youtube.com/get_video_info?video_id=%s' % video_id)
data = urllib.parse.parse_qs(str(info.read()))
info.close()
return data
@classmethod
def extract_video_id(cls, video_id):
if len(video_id) == VIDEO_ID_LEN:
return video_id
if video_id.startswith(VIDEO_ID_URL) and len(video_id) == len(VIDEO_ID_URL) + VIDEO_ID_LEN:
return video_id[len(VIDEO_ID_URL):]
raise ValueError("%r is a potential illegal YouTube video ID!" % video_id)
@property
def url(self):
return "http://youtube.com/watch?v=%s" % self.id
@property
def line(self):
return '%s\t%s\t%s\t%s\n' % (self.id, self.title, self.updated, self.author)
def __init__(self, line=None, id=None, title=None, updated=None, author=None):
if line is not None:
self.id, self.title, self.updated, self.author = line.rstrip().split("\t", 3)
else:
self.id = Video.extract_video_id(id)
self.title = title
self.updated = updated
self.author = author
if not self.id:
raise ValueError("No video ID given!")
if not self.updated:
# It's a pitty the YouTube API does not return video's uplaod timestamps,
# so use current date and time.
self.updated = now()
if not self.title or not self.author:
def oneof(self, *keys):
for key in keys:
val = self.get(key, None)
if val:
return val[0]
return None
# three tries to get the information successfully from the API
for i in range(2):
info = Video.get_info(self.id)
if not self.title:
# sometimes the keys look like a broken Python 2
# byte string literal
self.title = oneof(info, 'title', "b'title")
if not self.author:
self.author = oneof(info, 'author', "b'author")
# refetch data from API if sth. went wrong
if self.title and self.author:
break
sys.stderr.write("%s:\n%r" % (me, info))
else:
sys.stderr.write("%s: Cannot fetch metadata from YT API, tried 3 times!\n" % me)
@classmethod
def get_info(cls, video_id):
info = urllib.request.urlopen('http://youtube.com/get_video_info?video_id=%s' % video_id)
data = urllib.parse.parse_qs(str(info.read()))
info.close()
return data
@classmethod
def extract_video_id(cls, video_id):
if len(video_id) == VIDEO_ID_LEN:
return video_id
if video_id.startswith(VIDEO_ID_URL) and len(video_id) == len(VIDEO_ID_URL) + VIDEO_ID_LEN:
return video_id[len(VIDEO_ID_URL):]
raise ValueError("%r is a potential illegal YouTube video ID!" % video_id)
@property
def url(self):
return "http://youtube.com/watch?v=%s" % self.id
@property
def line(self):
return '%s\t%s\t%s\t%s\n' % (self.id, self.title, self.updated, self.author)
def add_fav(video_id):
video_id = Video.extract_video_id(video_id)
if not os.path.exists(INDEX_FILE):
index_dirname = os.path.dirname(INDEX_FILE)
if not os.path.exists(index_dirname):
os.makedirs(index_dirname)
else:
# filter duplicates
with open(INDEX_FILE, "r") as index:
for line in index.readlines():
video = Video(line=line)
if video.id == video_id:
return
video = Video(id=video_id)
with open(INDEX_FILE, "a") as index:
index.write(video.line)
video_id = Video.extract_video_id(video_id)
if not os.path.exists(INDEX_FILE):
index_dirname = os.path.dirname(INDEX_FILE)
if not os.path.exists(index_dirname):
os.makedirs(index_dirname)
else:
# filter duplicates
with open(INDEX_FILE, "r") as index:
for line in index.readlines():
video = Video(line=line)
if video.id == video_id:
return
video = Video(id=video_id)
with open(INDEX_FILE, "a") as index:
index.write(video.line)
def gen_atom(max=None):
if not os.path.exists(INDEX_FILE):
return
atom_dirname = os.path.dirname(ATOM_FILE)
if not os.path.exists(atom_dirname):
os.makedirs(atom_dirname)
with open(INDEX_FILE, "r") as index, open(ATOM_FILE if max is None else ATOM_42_FILE, "w") as atom:
atom.write('<?xml version="1.0" encoding="utf-8"?>'
'<feed xmlns="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">'
'<link rel="self" type="application/atom+xml" href="' + CONFIG.get("atom", "url", "https://lyse.isobeef.org/yfav.atom") + '" />'
'<id>' + CONFIG.get("atom", "id", CONFIG.get("atom", "url", "https://lyse.isobeef.org/yfav.atom")) + '</id>'
'<title>' + CONFIG.get("atom", "title", "Lyse's YouTube video catapult") + '</title>'
'<subtitle>' + CONFIG.get("atom", "subtitle", "Some awesome videos on YouTube which Lyse thinks are worth to spread. It's mainly a filtered bestofyoutube.com view. Normally updates every 1-2 days. Reduced feed with only latest 42 videos is here: https://lyse.isobeef.org/yfav.42.atom Feed with all videos is here: https://lyse.isobeef.org/yfav.atom") + '</subtitle>'
'<sy:updatePeriod>' + CONFIG.get("atom", "updatePeriod", "daily") + '</sy:updatePeriod>'
'<sy:updateFrequency>' + CONFIG.get("atom", "updateFrequency", "1") + '</sy:updateFrequency>'
'<updated>' + now() + '</updated>'
'<generator>yfav/' + __version__ + '</generator>\n')
lines = reversed(index.readlines())
if max is not None:
lines = list(lines)[:max]
for line in lines:
video = Video(line=line)
atom.write('<entry><id>' + esc(video.url) + '</id>'
'<title>' + esc(video.title) + '</title>'
'<updated>' + esc(video.updated) + '</updated>'
'<author><name>' + esc(video.author) + '</name></author>'
'<link rel="alternate" href="' + esc(video.url) + '" /></entry>\n')
atom.write('</feed>\n')
if not os.path.exists(INDEX_FILE):
return
atom_dirname = os.path.dirname(ATOM_FILE)
if not os.path.exists(atom_dirname):
os.makedirs(atom_dirname)
with open(INDEX_FILE, "r") as index, open(ATOM_FILE if max is None else ATOM_42_FILE, "w") as atom:
atom.write('<?xml version="1.0" encoding="utf-8"?>'
'<feed xmlns="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">'
'<link rel="self" type="application/atom+xml" href="' + CONFIG.get("atom", "url", "https://lyse.isobeef.org/yfav.atom") + '" />'
'<id>' + CONFIG.get("atom", "id", CONFIG.get("atom", "url", "https://lyse.isobeef.org/yfav.atom")) + '</id>'
'<title>' + CONFIG.get("atom", "title", "Lyse's YouTube video catapult") + '</title>'
'<subtitle>' + CONFIG.get("atom", "subtitle", "Some awesome videos on YouTube which Lyse thinks are worth to spread. It's mainly a filtered bestofyoutube.com view. Normally updates every 1-2 days. Reduced feed with only latest 42 videos is here: https://lyse.isobeef.org/yfav.42.atom Feed with all videos is here: https://lyse.isobeef.org/yfav.atom") + '</subtitle>'
'<sy:updatePeriod>' + CONFIG.get("atom", "updatePeriod", "daily") + '</sy:updatePeriod>'
'<sy:updateFrequency>' + CONFIG.get("atom", "updateFrequency", "1") + '</sy:updateFrequency>'
'<updated>' + now() + '</updated>'
'<generator>yfav/' + __version__ + '</generator>\n')
lines = reversed(index.readlines())
if max is not None:
lines = list(lines)[:max]
for line in lines:
video = Video(line=line)
atom.write('<entry><id>' + esc(video.url) + '</id>'
'<title>' + esc(video.title) + '</title>'
'<updated>' + esc(video.updated) + '</updated>'
'<author><name>' + esc(video.author) + '</name></author>'
'<link rel="alternate" href="' + esc(video.url) + '" /></entry>\n')
atom.write('</feed>\n')
def upload(video_id):
cmd = "ssh " + CONFIG.get("ssh", "user", "lyse") \
cmd = "ssh " + CONFIG.get("ssh", "user", "lyse") \
+ "@" + CONFIG.get("ssh", "host", "isobeef.org") \
+ " '" + CONFIG.get("ssh", "yfav", "~/bin/yfav") \
+ " " + video_id + "'"
e = os.system(cmd) % 255
if e != 0:
raise ValueError("%s\nexit code %d" % (cmd, e))
e = os.system(cmd) % 255
if e != 0:
raise ValueError("%s\nexit code %d" % (cmd, e))
if __name__ == "__main__":
import sys
import socket
hostname = os.getenv("HOST") or os.getenv("HOSTNMAE") or socket.gethostname()
if hostname == "isobeef.org":
process = add_fav
else:
process = upload
def nop(ignore=None):
pass
gen_atom = nop
if len(sys.argv) > 1:
for video_id in sys.argv[1:]:
process(video_id)
else:
if sys.stdin.isatty():
while True:
try:
video_id = input("YouTube Video ID > ")
if video_id:
process(video_id)
except EOFError:
break
else:
for video_id in sys.stdin.readlines():
process(video_id.strip())
gen_atom()
gen_atom(42)
import sys
import socket
hostname = os.getenv("HOST") or os.getenv("HOSTNMAE") or socket.gethostname()
if hostname == "isobeef.org":
process = add_fav
else:
process = upload
def nop(ignore=None):
pass
gen_atom = nop
if len(sys.argv) > 1:
for video_id in sys.argv[1:]:
process(video_id)
else:
if sys.stdin.isatty():
while True:
try:
video_id = input("YouTube Video ID > ")
if video_id:
process(video_id)
except EOFError:
break
else:
for video_id in sys.stdin.readlines():
process(video_id.strip())
gen_atom()
gen_atom(42)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment