media.py 6.51 KB
Newer Older
Bob Mottram's avatar
Bob Mottram committed
1 2 3
__filename__ = "media.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
Bob Mottram's avatar
Bob Mottram committed
4
__version__ = "1.2.0"
Bob Mottram's avatar
Bob Mottram committed
5 6 7
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
Bob Mottram's avatar
Bob Mottram committed
8 9 10

import os
import datetime
11
from hashlib import sha1
Bob Mottram's avatar
Bob Mottram committed
12
from auth import createPassword
Bob Mottram's avatar
Tidying  
Bob Mottram committed
13
from utils import getFullDomain
14 15 16 17
from utils import getImageExtensions
from utils import getVideoExtensions
from utils import getAudioExtensions
from utils import getMediaExtensions
Bob Mottram's avatar
Bob Mottram committed
18
from shutil import copyfile
Bob Mottram's avatar
Bob Mottram committed
19
from shutil import rmtree
Bob Mottram's avatar
Bob Mottram committed
20
from shutil import move
Bob Mottram's avatar
Bob Mottram committed
21

Bob Mottram's avatar
Bob Mottram committed
22

23 24
def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None:
    """Replace YouTube with a replacement domain
25 26
    This denies Google some, but not all, tracking data
    """
27 28
    if not replacementDomain:
        return
29 30 31 32 33 34
    if not isinstance(postJsonObject['object'], dict):
        return
    if not postJsonObject['object'].get('content'):
        return
    if 'www.youtube.com' not in postJsonObject['object']['content']:
        return
Bob Mottram's avatar
Bob Mottram committed
35 36
    postJsonObject['object']['content'] = \
        postJsonObject['object']['content'].replace('www.youtube.com',
37
                                                    replacementDomain)
Bob Mottram's avatar
Bob Mottram committed
38

39

Bob Mottram's avatar
Bob Mottram committed
40
def removeMetaData(imageFilename: str, outputFilename: str) -> None:
41 42
    """Attempts to do this with pure python didn't work well,
    so better to use a dedicated tool if one is installed
Bob Mottram's avatar
Bob Mottram committed
43
    """
Bob Mottram's avatar
Bob Mottram committed
44
    copyfile(imageFilename, outputFilename)
Bob Mottram's avatar
Bob Mottram committed
45 46 47
    if not os.path.isfile(outputFilename):
        print('ERROR: unable to remove metadata from ' + imageFilename)
        return
48
    if os.path.isfile('/usr/bin/exiftool'):
Bob Mottram's avatar
Bob Mottram committed
49
        print('Removing metadata from ' + outputFilename + ' using exiftool')
Bob Mottram's avatar
Tidying  
Bob Mottram committed
50
        os.system('exiftool -all= ' + outputFilename)  # nosec
51
    elif os.path.isfile('/usr/bin/mogrify'):
Bob Mottram's avatar
Bob Mottram committed
52
        print('Removing metadata from ' + outputFilename + ' using mogrify')
Bob Mottram's avatar
Tidying  
Bob Mottram committed
53
        os.system('/usr/bin/mogrify -strip ' + outputFilename)  # nosec
54

Bob Mottram's avatar
Bob Mottram committed
55

56
def _isMedia(imageFilename: str) -> bool:
57
    permittedMedia = getMediaExtensions()
Bob Mottram's avatar
Bob Mottram committed
58
    for m in permittedMedia:
Bob Mottram's avatar
Bob Mottram committed
59
        if imageFilename.endswith('.' + m):
Bob Mottram's avatar
Bob Mottram committed
60
            return True
Bob Mottram's avatar
Bob Mottram committed
61
    print('WARN: ' + imageFilename + ' is not a permitted media type')
Bob Mottram's avatar
Bob Mottram committed
62 63
    return False

Bob Mottram's avatar
Bob Mottram committed
64 65 66 67 68 69 70

def createMediaDirs(baseDir: str, mediaPath: str) -> None:
    if not os.path.isdir(baseDir + '/media'):
        os.mkdir(baseDir + '/media')
    if not os.path.isdir(baseDir + '/' + mediaPath):
        os.mkdir(baseDir + '/' + mediaPath)

Bob Mottram's avatar
Bob Mottram committed
71

Bob Mottram's avatar
Bob Mottram committed
72
def getMediaPath() -> str:
Bob Mottram's avatar
Bob Mottram committed
73 74 75 76
    currTime = datetime.datetime.utcnow()
    weeksSinceEpoch = int((currTime - datetime.datetime(1970, 1, 1)).days / 7)
    return 'media/' + str(weeksSinceEpoch)

Bob Mottram's avatar
Bob Mottram committed
77 78 79 80 81

def getAttachmentMediaType(filename: str) -> str:
    """Returns the type of media for the given file
    image, video or audio
    """
Bob Mottram's avatar
Bob Mottram committed
82
    mediaType = None
83
    imageTypes = getImageExtensions()
Bob Mottram's avatar
Bob Mottram committed
84
    for mType in imageTypes:
Bob Mottram's avatar
Bob Mottram committed
85
        if filename.endswith('.' + mType):
Bob Mottram's avatar
Bob Mottram committed
86
            return 'image'
87
    videoTypes = getVideoExtensions()
Bob Mottram's avatar
Bob Mottram committed
88
    for mType in videoTypes:
Bob Mottram's avatar
Bob Mottram committed
89
        if filename.endswith('.' + mType):
Bob Mottram's avatar
Bob Mottram committed
90
            return 'video'
91
    audioTypes = getAudioExtensions()
Bob Mottram's avatar
Bob Mottram committed
92
    for mType in audioTypes:
Bob Mottram's avatar
Bob Mottram committed
93
        if filename.endswith('.' + mType):
Bob Mottram's avatar
Bob Mottram committed
94 95 96
            return 'audio'
    return mediaType

Bob Mottram's avatar
Bob Mottram committed
97

98
def _updateEtag(mediaFilename: str) -> None:
99 100 101 102 103 104 105 106 107 108 109
    """ calculate the etag, which is a sha1 of the data
    """
    # only create etags for media
    if '/media/' not in mediaFilename:
        return

    # check that the media exists
    if not os.path.isfile(mediaFilename):
        return

    # read the binary data
Bob Mottram's avatar
Bob Mottram committed
110
    data = None
111
    try:
Bob Mottram's avatar
Extra +  
Bob Mottram committed
112
        with open(mediaFilename, 'rb') as mediaFile:
Bob Mottram's avatar
Bob Mottram committed
113 114
            data = mediaFile.read()
    except BaseException:
115 116 117 118 119
        pass

    if not data:
        return
    # calculate hash
Bob Mottram's avatar
Tidying  
Bob Mottram committed
120
    etag = sha1(data).hexdigest()  # nosec
121 122
    # save the hash
    try:
Bob Mottram's avatar
Bob Mottram committed
123
        with open(mediaFilename + '.etag', 'w+') as etagFile:
124
            etagFile.write(etag)
Bob Mottram's avatar
Bob Mottram committed
125
    except BaseException:
126 127
        pass

Bob Mottram's avatar
Bob Mottram committed
128 129 130

def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
                postJson: {}, imageFilename: str,
Bob Mottram's avatar
Bob Mottram committed
131
                mediaType: str, description: str) -> {}:
Bob Mottram's avatar
Bob Mottram committed
132
    """Attaches media to a json object post
Bob Mottram's avatar
Bob Mottram committed
133 134
    The description can be None
    """
135
    if not _isMedia(imageFilename):
Bob Mottram's avatar
Bob Mottram committed
136
        return postJson
Bob Mottram's avatar
Bob Mottram committed
137

Bob Mottram's avatar
Bob Mottram committed
138
    fileExtension = None
139
    acceptedTypes = getMediaExtensions()
Bob Mottram's avatar
Bob Mottram committed
140
    for mType in acceptedTypes:
Bob Mottram's avatar
Bob Mottram committed
141 142 143 144 145 146
        if imageFilename.endswith('.' + mType):
            if mType == 'jpg':
                mType = 'jpeg'
            if mType == 'mp3':
                mType = 'mpeg'
            fileExtension = mType
Bob Mottram's avatar
Bob Mottram committed
147
    if not fileExtension:
Bob Mottram's avatar
Bob Mottram committed
148
        return postJson
Bob Mottram's avatar
Bob Mottram committed
149 150
    mediaType = mediaType + '/' + fileExtension
    print('Attached media type: ' + mediaType)
Bob Mottram's avatar
Bob Mottram committed
151

Bob Mottram's avatar
Bob Mottram committed
152 153 154 155
    if fileExtension == 'jpeg':
        fileExtension = 'jpg'
    if mediaType == 'audio/mpeg':
        fileExtension = 'mp3'
Bob Mottram's avatar
Bob Mottram committed
156

Bob Mottram's avatar
Tidying  
Bob Mottram committed
157
    domain = getFullDomain(domain, port)
Bob Mottram's avatar
Bob Mottram committed
158

Bob Mottram's avatar
Bob Mottram committed
159 160
    mPath = getMediaPath()
    mediaPath = mPath + '/' + createPassword(32) + '.' + fileExtension
Bob Mottram's avatar
Bob Mottram committed
161
    if baseDir:
Bob Mottram's avatar
Bob Mottram committed
162 163
        createMediaDirs(baseDir, mPath)
        mediaFilename = baseDir + '/' + mediaPath
Bob Mottram's avatar
Bob Mottram committed
164

Bob Mottram's avatar
Bob Mottram committed
165
    attachmentJson = {
Bob Mottram's avatar
Bob Mottram committed
166 167
        'mediaType': mediaType,
        'name': description,
Bob Mottram's avatar
Bob Mottram committed
168
        'type': 'Document',
Bob Mottram's avatar
Bob Mottram committed
169
        'url': httpPrefix + '://' + domain + '/' + mediaPath
Bob Mottram's avatar
Bob Mottram committed
170
    }
Bob Mottram's avatar
Bob Mottram committed
171
    if mediaType.startswith('image/'):
Bob Mottram's avatar
Bob Mottram committed
172 173
        attachmentJson['focialPoint'] = [0.0, 0.0]
    postJson['attachment'] = [attachmentJson]
Bob Mottram's avatar
Bob Mottram committed
174

Bob Mottram's avatar
Bob Mottram committed
175
    if baseDir:
Bob Mottram's avatar
Bob Mottram committed
176
        if mediaType.startswith('image/'):
Bob Mottram's avatar
Bob Mottram committed
177
            removeMetaData(imageFilename, mediaFilename)
Bob Mottram's avatar
Bob Mottram committed
178
        else:
Bob Mottram's avatar
Bob Mottram committed
179
            copyfile(imageFilename, mediaFilename)
180
        _updateEtag(mediaFilename)
181

Bob Mottram's avatar
Bob Mottram committed
182 183
    return postJson

Bob Mottram's avatar
Bob Mottram committed
184 185

def archiveMedia(baseDir: str, archiveDirectory: str, maxWeeks=4) -> None:
Bob Mottram's avatar
Bob Mottram committed
186 187
    """Any media older than the given number of weeks gets archived
    """
Bob Mottram's avatar
Tidying  
Bob Mottram committed
188 189 190
    if maxWeeks == 0:
        return

Bob Mottram's avatar
Bob Mottram committed
191 192
    currTime = datetime.datetime.utcnow()
    weeksSinceEpoch = int((currTime - datetime.datetime(1970, 1, 1)).days/7)
Bob Mottram's avatar
Tidying  
Bob Mottram committed
193
    minWeek = weeksSinceEpoch - maxWeeks
Bob Mottram's avatar
Bob Mottram committed
194

Bob Mottram's avatar
Bob Mottram committed
195 196 197
    if archiveDirectory:
        if not os.path.isdir(archiveDirectory):
            os.mkdir(archiveDirectory)
Bob Mottram's avatar
Bob Mottram committed
198 199
        if not os.path.isdir(archiveDirectory + '/media'):
            os.mkdir(archiveDirectory + '/media')
Bob Mottram's avatar
Bob Mottram committed
200

Bob Mottram's avatar
Bob Mottram committed
201
    for subdir, dirs, files in os.walk(baseDir + '/media'):
Bob Mottram's avatar
Bob Mottram committed
202
        for weekDir in dirs:
Bob Mottram's avatar
Bob Mottram committed
203
            if int(weekDir) < minWeek:
Bob Mottram's avatar
Bob Mottram committed
204
                if archiveDirectory:
Bob Mottram's avatar
Bob Mottram committed
205 206
                    move(os.path.join(baseDir + '/media', weekDir),
                         archiveDirectory + '/media')
Bob Mottram's avatar
Bob Mottram committed
207 208
                else:
                    # archive to /dev/null
Bob Mottram's avatar
Bob Mottram committed
209
                    rmtree(os.path.join(baseDir + '/media', weekDir))
Bob Mottram's avatar
Bob Mottram committed
210
        break