Media timeline

main2
Bob Mottram 2019-09-28 12:29:42 +01:00
parent 160f272b5d
commit 8ed3f132be
4 changed files with 183 additions and 12 deletions

View File

@ -85,6 +85,7 @@ from webinterface import htmlAbout
from webinterface import htmlRemoveSharedItem
from webinterface import htmlInboxDMs
from webinterface import htmlInboxReplies
from webinterface import htmlInboxMedia
from webinterface import htmlUnblockConfirm
from webinterface import htmlPersonOptions
from webinterface import htmlIndividualPost
@ -1478,6 +1479,7 @@ class PubServer(BaseHTTPRequestHandler):
self._404()
self.server.GETbusy=False
return
# get replies to a post /users/nickname/statuses/number/replies
if self.path.endswith('/replies') or '/replies?page=' in self.path:
if '/statuses/' in self.path and '/users/' in self.path:
@ -1941,6 +1943,77 @@ class PubServer(BaseHTTPRequestHandler):
self.server.GETbusy=False
return
# get the media for a given person
if self.path.endswith('/tlmedia') or '/tlmedia?page=' in self.path:
if '/users/' in self.path:
if authorized:
inboxMediaFeed= \
personBoxJson(self.server.baseDir, \
self.server.domain, \
self.server.port, \
self.path, \
self.server.httpPrefix, \
maxPostsInFeed, 'tlmedia', \
True,self.server.ocapAlways)
if not inboxMediaFeed:
inboxMediaFeed=[]
if self._requestHTTP():
nickname=self.path.replace('/users/','').replace('/tlmedia','')
pageNumber=1
if '?page=' in nickname:
pageNumber=nickname.split('?page=')[1]
nickname=nickname.split('?page=')[0]
if pageNumber.isdigit():
pageNumber=int(pageNumber)
else:
pageNumber=1
if 'page=' not in self.path:
# if no page was specified then show the first
inboxMediaFeed= \
personBoxJson(self.server.baseDir, \
self.server.domain, \
self.server.port, \
self.path+'?page=1', \
self.server.httpPrefix, \
maxPostsInFeed, 'tlmedia', \
True,self.server.ocapAlways)
msg=htmlInboxMedia(self.server.translate, \
pageNumber,maxPostsInFeed, \
self.server.session, \
self.server.baseDir, \
self.server.cachedWebfingers, \
self.server.personCache, \
nickname, \
self.server.domain, \
self.server.port, \
inboxMediaFeed, \
self.server.allowDeletion, \
self.server.httpPrefix, \
self.server.projectVersion).encode('utf-8')
self._set_headers('text/html',len(msg),cookie)
self.wfile.write(msg)
else:
# don't need authenticated fetch here because there is
# already the authorization check
msg=json.dumps(inboxMediaFeed).encode('utf-8')
self._set_headers('application/json',len(msg),None)
self.wfile.write(msg)
self.server.GETbusy=False
return
else:
if self.server.debug:
nickname=self.path.replace('/users/','').replace('/tlmedia','')
print('DEBUG: '+nickname+ \
' was not authorized to access '+self.path)
if self.path!='/tlmedia':
# not the media inbox
if self.server.debug:
print('DEBUG: GET access to inbox is unauthorized')
self.send_response(405)
self.end_headers()
self.server.GETbusy=False
return
# get outbox feed for a person
outboxFeed=personBoxJson(self.server.baseDir,self.server.domain, \
self.server.port,self.path, \

View File

@ -28,6 +28,8 @@
--button-height: 10px;
--button-height-padding-mobile: 20px;
--button-height-padding: 10px;
--gallery-border: #ccc;
--gallery-hover: #777;
}
body, html {
@ -760,10 +762,35 @@ input[type=checkbox]
transform: translateY(-10%);
}
div.gallery {
margin: 5px;
border: 1px solid var(--gallery-border);
float: left;
width: 180px;
}
div.gallery:hover {
border: 1px solid var(--gallery-hover);
}
div.gallery img {
width: 100%;
height: auto;
}
div.imagedesc {
padding: 22px;
text-align: center;
}
@media screen and (min-width: 400px) {
body, html {
font-size: 22px;
}
div.imagedesc {
padding: 22px;
text-align: center;
}
.container img {
float: left;
max-width: 400px;
@ -989,6 +1016,10 @@ input[type=checkbox]
body, html {
font-size: 35px;
}
div.imagedesc {
padding: 35px;
text-align: center;
}
.container img {
float: left;
max-width: 400px;

View File

@ -1585,6 +1585,11 @@ def createRepliesTimeline(baseDir: str,nickname: str,domain: str,port: int,httpP
return createBoxBase(baseDir,'tlreplies',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
def createMediaTimeline(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}:
return createBoxBase(baseDir,'tlmedia',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
itemsPerPage: int,headerOnly: bool,authorized: bool,pageNumber=None) -> {}:
return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \
@ -1684,6 +1689,30 @@ def isDM(postJsonObject: {}) -> bool:
return False
return True
def isImageMedia(postJsonObject: {}) -> bool:
"""Returns true if the given post has attached image media
"""
if postJsonObject['type']!='Create':
return False
if not postJsonObject.get('object'):
return False
if not isinstance(postJsonObject['object'], dict):
return False
if postJsonObject['object']['type']!='Note':
return False
if not postJsonObject['object'].get('attachment'):
return False
if not isinstance(postJsonObject['object']['attachment'], list):
return False
if len(postJsonObject['object']['attachment'])==0:
return False
for attach in postJsonObject['object']['attachment']:
if attach.get('mediaType') and attach.get('url'):
mediaType=attach['mediaType']
if mediaType.startswith('image/'):
return True
return False
def isReply(postJsonObject: {},actor: str) -> bool:
"""Returns true if the given post is a reply to the given actor
"""
@ -1707,15 +1736,16 @@ def createBoxBase(baseDir: str,boxname: str, \
ocapAlways: bool,pageNumber=None) -> {}:
"""Constructs the box feed for a person with the given nickname
"""
if boxname!='inbox' and boxname!='dm' and boxname!='tlreplies' and boxname!='outbox':
if boxname!='inbox' and boxname!='dm' and \
boxname!='tlreplies' and boxname!='tlmedia' and boxname!='outbox':
return None
if boxname!='dm' and boxname!='tlreplies':
if boxname!='dm' and boxname!='tlreplies' and boxname!='tlmedia':
boxDir = createPersonDir(nickname,domain,baseDir,boxname)
else:
# extract DMs or replies from the inbox
boxDir = createPersonDir(nickname,domain,baseDir,'inbox')
sharedBoxDir=None
if boxname=='inbox' or boxname=='tlreplies':
if boxname=='inbox' or boxname=='tlreplies' or boxname=='tlmedia':
sharedBoxDir = createPersonDir('inbox',domain,baseDir,boxname)
if port:
@ -1865,7 +1895,7 @@ def createBoxBase(baseDir: str,boxname: str, \
# get the post as json
p = json.loads(postStr)
if (boxname!='dm' and boxname!='tlreplies'):
if (boxname!='dm' and boxname!='tlreplies' and boxname!='tlmedia'):
isTimelinePost=True
else:
if boxname=='dm':
@ -1874,6 +1904,9 @@ def createBoxBase(baseDir: str,boxname: str, \
elif boxname=='tlreplies':
if isDM(p) or isReply(p,boxActor):
isTimelinePost=True
elif boxname=='tlmedia':
if isImageMedia(p):
isTimelinePost=True
if isTimelinePost and currPage == pageNumber:
# remove any capability so that it's not displayed
@ -2084,7 +2117,9 @@ def sendCapabilitiesUpdate(session,baseDir: str,httpPrefix: str, \
sendThreads,postLog,cachedWebfingers, \
personCache,debug,projectVersion)
def populateRepliesJson(baseDir: str,nickname: str,domain: str,postRepliesFilename: str,authorized: bool,repliesJson: {}) -> None:
def populateRepliesJson(baseDir: str,nickname: str,domain: str, \
postRepliesFilename: str,authorized: bool, \
repliesJson: {}) -> None:
# populate the items list with replies
repliesBoxes=['outbox','inbox']
with open(postRepliesFilename,'r') as repliesFile:

View File

@ -1666,6 +1666,7 @@ def individualPostAsHtml(iconsDir: str,translate: {}, \
showDMicon=True
titleStr=''
galleryStr=''
isAnnounced=False
if postJsonObject['type']=='Announce':
if postJsonObject.get('object'):
@ -1875,6 +1876,17 @@ def individualPostAsHtml(iconsDir: str,translate: {}, \
attach['url'].endswith('.gif'):
if attachmentCtr>0:
attachmentStr+='<br>'
if boxName=='tlmedia':
galleryStr+= \
'<div class="gallery">\n' \
' <a target="_blank" href="'+attach['url']+'">\n' \
' <img src="'+attach['url']+'" alt="'+imageDescription+'" title="'+imageDescription+'" width="600" height="400">\n' \
' </a>\n'
if postJsonObject['object'].get('content'):
galleryStr+= \
' <div class="imagedesc">'+postJsonObject['object']['content']+'</div>\n'
galleryStr+= \
'</div>\n' \
attachmentStr+= \
'<a href="'+attach['url']+'">' \
'<img src="'+attach['url']+'" alt="'+imageDescription+'" title="'+imageDescription+'" class="attachment"></a>\n'
@ -2064,12 +2076,15 @@ def individualPostAsHtml(iconsDir: str,translate: {}, \
contentStr='<div class="message">'+contentStr+'</div>'
return \
'<div class="'+containerClass+'">\n'+ \
avatarImageInPost+ \
'<p class="post-title">'+titleStr+replyAvatarImageInPost+'</p>'+ \
contentStr+footerStr+ \
'</div>\n'
if boxName!='tlmedia':
return \
'<div class="'+containerClass+'">\n'+ \
avatarImageInPost+ \
'<p class="post-title">'+titleStr+replyAvatarImageInPost+'</p>'+ \
contentStr+footerStr+ \
'</div>\n'
else:
return galleryStr
def isQuestion(postObjectJson: {}) -> bool:
""" is the given post a question?
@ -2106,6 +2121,7 @@ def htmlTimeline(translate: {},pageNumber: int, \
inboxButton='button'
dmButton='button'
repliesButton='button'
mediaButton='button'
sentButton='button'
moderationButton='button'
if boxName=='inbox':
@ -2114,6 +2130,8 @@ def htmlTimeline(translate: {},pageNumber: int, \
dmButton='buttonselected'
elif boxName=='tlreplies':
repliesButton='buttonselected'
elif boxName=='tlmedia':
mediaButton='buttonselected'
elif boxName=='outbox':
sentButton='buttonselected'
elif boxName=='moderation':
@ -2125,7 +2143,8 @@ def htmlTimeline(translate: {},pageNumber: int, \
showIndividualPostIcons=True
followApprovals=''
followRequestsFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/followrequests.txt'
followRequestsFilename= \
baseDir+'/accounts/'+nickname+'@'+domain+'/followrequests.txt'
if os.path.isfile(followRequestsFilename):
with open(followRequestsFilename,'r') as f:
for line in f:
@ -2160,6 +2179,7 @@ def htmlTimeline(translate: {},pageNumber: int, \
' <a href="'+actor+'/inbox"><button class="'+inboxButton+'"><span>'+translate['Inbox']+'</span></button></a>' \
' <a href="'+actor+'/dm"><button class="'+dmButton+'"><span>'+translate['DM']+'</span></button></a>' \
' <a href="'+actor+'/tlreplies"><button class="'+repliesButton+'"><span>'+translate['Replies']+'</span></button></a>' \
' <a href="'+actor+'/tlmedia"><button class="'+mediaButton+'"><span>'+translate['Media']+'</span></button></a>' \
' <a href="'+actor+'/outbox"><button class="'+sentButton+'"><span>'+translate['Outbox']+'</span></button></a>'+ \
moderationButtonStr+newPostButtonStr+ \
' <a href="'+actor+'/search"><img src="/'+iconsDir+'/search.png" title="'+translate['Search and follow']+'" alt="'+translate['Search and follow']+'" class="right"/></a>'+ \
@ -2250,6 +2270,18 @@ def htmlInboxReplies(translate: {},pageNumber: int,itemsPerPage: int, \
nickname,domain,port,inboxJson,'tlreplies',allowDeletion, \
httpPrefix,projectVersion,False)
def htmlInboxMedia(translate: {},pageNumber: int,itemsPerPage: int, \
session,baseDir: str,wfRequest: {},personCache: {}, \
nickname: str,domain: str,port: int,inboxJson: {}, \
allowDeletion: bool, \
httpPrefix: str,projectVersion: str) -> str:
"""Show the media timeline as html
"""
return htmlTimeline(translate,pageNumber, \
itemsPerPage,session,baseDir,wfRequest,personCache, \
nickname,domain,port,inboxJson,'tlmedia',allowDeletion, \
httpPrefix,projectVersion,False)
def htmlModeration(translate: {},pageNumber: int,itemsPerPage: int, \
session,baseDir: str,wfRequest: {},personCache: {}, \
nickname: str,domain: str,port: int,inboxJson: {}, \