Skip to content

Commit

Permalink
Cleaned up code (#2)
Browse files Browse the repository at this point in the history
* Cleaned up code

* Cleaned up more code

* Moved media folder config to seperate file
  • Loading branch information
dereisele authored and derolf committed Jul 25, 2017
1 parent 994c6b8 commit 059299e
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 68 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cache/
.idea/
private/
__pycache__
*.pyc


mediaconf.py
4 changes: 1 addition & 3 deletions library.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export LANG='en_US.UTF-8'
export LC_ALL='en_US.UTF-8'
export PATH=~/bin:$PATH
/usr/local/opt/python-3.4/bin/python3 library/server.py


/usr/bin/python3 library/server.py
27 changes: 12 additions & 15 deletions library/config.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
port= 8124

# library config

media_folder= "/mnt/stuff/Public/"
root_items= [ { "name": "Eltern", "target": "Shared Videos/Videos/Eltern" }, { "name": "Kinder", "target": "Shared Videos/Videos/Kinder" }, { "name": "Music", "target": "Shared Music" }, {"name": "Test Music", "target": "/home/daniel/Music"}, {"name": "Test Video", "target": "/home/daniel/TEST"} ]

media_folder= "/mnt/Public/"
root_items= [ { "name": "Eltern", "target": "Shared Videos/Videos/Eltern" }, { "name": "Aufnahmen", "target": "Shared Videos/Aufnahmen2" }, { "name": "Kinder", "target": "Shared Videos/Videos/Kinder" }, { "name": "Music", "target": "Shared Music" }, { "name": "Fotos", "target": "Shared Pictures" } ]
"""Config file."""

port = 8124

# transcoder config

ffmpeg= "ffmpeg"
ffmpeg = "ffmpeg"

types = {
"mp3": "audio",
"jpg": "image",
"mp4": "video"}


transcode_mime = {
"*" : "video/mp4",
transcode_mime = {
"*": "video/mp4",
"mp3": "audio/mp3",
"jpg": "image/jpg"}


ffmpeg_transcode_args_2 = {
"*": ["-f", "mp4", "-strict", "experimental", "-preset", "ultrafast", "-movflags", "frag_keyframe+empty_moov+faststart", "pipe:1"],
"mp3": ["-f", "mp3", "-codec", "copy", "pipe:1"]}

ffmpeg_transcode_args = {
"*" : [ "-f", "mp4", "-strict", "experimental", "-preset", "ultrafast", "-movflags", "frag_keyframe+empty_moov+faststart", "pipe:1" ],
"mp3": [ "-f", "mp3", "-codec", "copy", "pipe:1" ] }
"*": " -ss {} -i {} -f {} -vcodec {} -acodec {} -strict experimental -preset ultrafast -movflags frag_keyframe+empty_moov+faststart pipe:1",
"mp3": ["-f", "mp3", "-codec", "copy", "pipe:1"]}

ffmpeg_poster_args = [ "-f", "mjpeg", "-vf", "scale=512x512", "pipe:1" ]
ffmpeg_poster_args = ["-f", "mjpeg", "-vf", "scale=512x512", "pipe:1"]
# "-noaccurate_seek"
88 changes: 50 additions & 38 deletions library/library.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
cacheVersion = 4
"""Deals with metadata and icons."""

import threading
import os, subprocess, re
import os
import subprocess
import re
import config
import mediaconf
import hashlib
import pickle
import urllib
import time
from threading import Lock

uniqueLock = Lock();
cacheVersion = 4
uniqueLock = Lock()
counter = 0


def utf8(s):
"""Convert to UTF-8."""
return s.encode("UTF-8")


def unique():
"""Return unique identification number."""
global uniqueLock
global counter
with uniqueLock:
Expand All @@ -26,6 +32,7 @@ def unique():


def mapPath(path):
"""Map web path to file path."""
# extract root folder
path = os.path.normpath(path)
path = path.split("/")
Expand All @@ -34,17 +41,18 @@ def mapPath(path):

# map root folder
base = None
for item in config.root_items:
for item in mediaconf.root_items:
if item["name"] == root:
base = item["target"]
break
if base is None:
raise FileNotFoundError()

return os.path.join(config.media_folder, base + "/" + path if path != "" else base)
return os.path.join(mediaconf.media_folder, base + "/" + path if path != "" else base)


def indexFile(path):
"""Index File and creat metadata file."""
d = mapPath(path)
key = "cache/" + hashlib.md5(utf8(path)).hexdigest() + ".meta"

Expand Down Expand Up @@ -72,14 +80,15 @@ def indexFile(path):


def extractFrameAsJPG(d, start):
"""Return frame of video as jpeg."""
cmdline = list()
cmdline.append(config.ffmpeg)
cmdline.append("-ss")
cmdline.append(str(start))
cmdline.append("-i")
cmdline.append(d);
cmdline.append(d)
cmdline.append("-vframes")
cmdline.append("1");
cmdline.append("1")
cmdline.extend(config.ffmpeg_poster_args)
FNULL = open(os.devnull, 'w')
proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=FNULL)
Expand All @@ -96,6 +105,7 @@ def extractFrameAsJPG(d, start):


def icon(path):
"""Read and yield chunks of icon file."""
infile = iconFile(path)
try:
byte = infile.read(65536)
Expand All @@ -107,6 +117,7 @@ def icon(path):


def iconFile(path):
"""Read and create icon file."""
key = "cache/" + hashlib.md5(utf8(path)).hexdigest() + ".icon"

d = mapPath(path)
Expand Down Expand Up @@ -139,7 +150,6 @@ def iconFile(path):
# couldn't find any jpg, try the folders
for f in os.listdir(d):
if not os.path.isfile(f):
empty = True
sub = iconFile(path + "/" + f)
if len(sub.peek(1)) > 0:
return sub
Expand All @@ -156,6 +166,7 @@ def iconFile(path):


def libraryMeta(path, details):
"""Provide meatadata for given path."""
meta = dict()

# handle root
Expand Down Expand Up @@ -187,12 +198,13 @@ def libraryMeta(path, details):


def library(path):
"""Wrapp for libraryMeta."""
meta = libraryMeta(path, True)

# handle root
if path == "":
items = []
for item in config.root_items:
for item in mediaconf.root_items:
child = libraryMeta(item["name"], False)
items.append(child)
meta["items"] = items
Expand All @@ -210,17 +222,14 @@ def library(path):

return meta


def getType(path):
d = mapPath(path)


def getDuration(path):
"""Figure out duration of a media file using FFmpeg."""
d = mapPath(path)
cmdline = list()
cmdline.append(config.ffmpeg)
cmdline.append("-i")
cmdline.append(d);
cmdline.append(d)
duration = -1
FNULL = open(os.devnull, 'w')
proc = subprocess.Popen(cmdline, stderr=subprocess.PIPE, stdout=FNULL)
Expand All @@ -238,23 +247,26 @@ def getDuration(path):


def transcodeMime(format):
"""Translate file format to Mime type."""
return config.transcode_mime.get(format) or config.transcode_mime["*"]


def transcode(path, start, format, vcodec, acodec):
"""Transcode in ffmpeg subprocess."""
d = mapPath(path)
dummy, ext = os.path.splitext(d)
_, ext = os.path.splitext(d)
ext = ext[1:]
args = config.ffmpeg_transcode_args.get(ext) or config.ffmpeg_transcode_args["*"]
cmdline = list()
cmdline.append(config.ffmpeg)
cmdline.append("-ss")
cmdline.append(str(start))
cmdline.append("-i")
cmdline.append(d)
cmdline.append("-f")
cmdline.append(format)
if vcodec:
# args = config.ffmpeg_transcode_args.get(ext) or
args = config.ffmpeg_transcode_args["*"]

cmdline = config.ffmpeg + args.format(str(start), d, format, vcodec, acodec)

# TODO:
# make cmdline dynamic
# add -vn
# make acodec optional

"""if vcodec:
if vcodec == "none":
cmdline.append("-vn")
else:
Expand All @@ -263,17 +275,12 @@ def transcode(path, start, format, vcodec, acodec):
if acodec:
cmdline.append("-acodec")
cmdline.append(acodec)
cmdline.append("-strict")
cmdline.append("experimental")
cmdline.append("-preset")
cmdline.append("ultrafast")
cmdline.append("-movflags")
cmdline.append("empty_moov+faststart+frag_keyframe") # +
cmdline.append("pipe:1")
print(" ".join(cmdline))
"""

FNULL = open(os.devnull, 'w')
proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE)#, stderr=FNULL)
print(cmdline)

# FNULL = open(os.devnull, 'w')
proc = subprocess.Popen(cmdline.split(), stdout=subprocess.PIPE) # , stderr=FNULL)
try:
f = proc.stdout
byte = f.read(65536)
Expand All @@ -285,23 +292,28 @@ def transcode(path, start, format, vcodec, acodec):


def refreshLoop():
"""Refresh library every hour."""
def refresh(path):
"""Refresh path (file and directories)."""
iconFile(path).close()
entry = library(path)

if not entry["file"]:
# Recrusive indexing
for child in entry["items"]:
refresh(child["path"])

while True:
print("Refreshing")
items = []
for item in config.root_items:
for item in mediaconf.root_items:
refresh(item["name"])
print("Refresh done")

time.sleep(3600)


def init():
"""Start library loading in new thread."""
t = threading.Thread(target=refreshLoop)
t.daemon = True # stop if the program exits
t.daemon = True # stop if the program exits
t.start()
8 changes: 8 additions & 0 deletions library/mediaconf.py.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Config file for media folder."""

"""for usage rename to mediaconf.py"""

media_folder = "/media/hdd1/TV"
root_items = [{"name": "The-Flash", "target": "/media/hdd1/TV/The-Flash"},
{"name": "Test", "target": "/home/alexander/tmp/test-media"},
{"name": "TBBT", "target": "/media/hdd1/TV/The-Big-Bang-Theory"}]
9 changes: 5 additions & 4 deletions library/restlibrary.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Rest api."""
from flask import jsonify, request, Response, abort
import library as L
import json
Expand All @@ -8,20 +9,20 @@
FileNotFoundError = IOError



def init(app):
"""Init restapi."""
L.init()

@app.route("/library/", defaults={"path": ""})
@app.route("/library/<path:path>")
@app.route("/library/<path:path>") # Returns metadata of path in json format
def library(path):
try:
return jsonify(L.library(path)), 200, {'Access-Control-Allow-Origin': '*'}
except IOError:
abort(404)

@app.route('/media/<path:path>.<regex("\w+"):format>')
def media_content_tc(path, format):
def media_content_tc(path, format): # Returns media file
start = float(request.args.get("start") or 0)
vcodec = request.args.get("vcodec")
acodec = request.args.get("acodec")
Expand All @@ -34,7 +35,7 @@ def media_content_tc(path, format):
abort(404)

@app.route('/icon/<path:path>.jpg')
def media_content_icon(path):
def media_content_icon(path): # returns icon file
try:
return Response(response=L.icon(path), status=200, mimetype='image/jpg',
headers={'Access-Control-Allow-Origin': '*', "Content-Type": "image/jpg",
Expand Down
15 changes: 9 additions & 6 deletions library/server.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
from flask import Flask, request, Response, abort, send_file, jsonify
import os, subprocess, re
"""Main file and sets up flask."""

from flask import Flask
import config as C
import library as L
import web
import restlibrary
from werkzeug.routing import BaseConverter

app = Flask(__name__)


# Initialize the Flask application
app = Flask(__name__)


class RegexConverter(BaseConverter):
"""Regex Converter for routing in Flask."""

def __init__(self, url_map, *items):
"""Init RegexConverter."""
super(RegexConverter, self).__init__(url_map)
self.regex = items[0]

Expand All @@ -24,13 +25,15 @@ def __init__(self, url_map, *items):

@app.after_request
def add_header(response):
"""Add header to response."""
response.cache_control.max_age = 300
response.cache_control.no_cache = True
response.cache_control.must_revalidate = True
response.cache_control.proxy_revalidate = True
return response


restlibrary.init(app)
web.init(app)

app.run(host="0.0.0.0",port=C.port, threaded=True, debug=False)
app.run(host="0.0.0.0", port=C.port, threaded=True, debug=False)
Loading

0 comments on commit 059299e

Please sign in to comment.