Version 114

This commit is contained in:
Hydrus 2014-05-07 17:42:30 -05:00
parent 9f5a441c08
commit 21bd1e15fe
13 changed files with 131 additions and 15 deletions

View File

@ -17,6 +17,7 @@ from include import HydrusConstants as HC
import os
import sys
from include import ClientController
import threading
from twisted.internet import reactor

View File

@ -8,6 +8,16 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 114</h3></li>
<ul>
<li>gif rendering seems to be fixed in all cases! hooray!</li>
<li>fixed 'pop from empty list' popup error spam in the new cache clearing system</li>
<li>fixed weird behaviour on right-clicking 'dismiss all' popups button</li>
<li>added a unit test for perceptual hash generation, with an eye to moving the delicate code from cv to cv2</li>
<li>updated opencv to 2.4.9</li>
<li>updated pyinstaller, so frozen releases may be a bit more stable!</li>
<li>moved webm and mkv info parsing over to cv, which allows num_frames</li>
</ul>
<li><h3>version 113</h3></li>
<ul>
<li>added mkv+webm support!</li>

View File

@ -1348,6 +1348,10 @@ class DataCache():
del self._keys_fifo[ 0 ]
data = self._keys_to_data[ key ]
self._total_estimated_memory_footprint -= data.GetEstimatedMemoryFootprint()
del self._keys_to_data[ key ]
else: break

View File

@ -1319,6 +1319,13 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
service_type = service_identifier.GetType()
name = service_identifier.GetName()
if service_type in HC.CLIENT_SERVICES:
if 'max_monthly_data' not in info: info[ 'max_monthly_data' ] = None
if 'port' not in info: info[ 'port' ] = 45871
if 'upnp' not in info: info[ 'upnp' ] = None
if service_type in HC.REMOTE_SERVICES:
if 'last_error' not in info: info[ 'last_error' ] = 0

View File

@ -2195,6 +2195,7 @@ class FrameReviewServices( ClientGUICommon.Frame ):
elif service_type == HC.SERVER_ADMIN: name = 'servers admin'
elif service_type == HC.LOCAL_RATING_LIKE: name = 'local ratings like'
elif service_type == HC.LOCAL_RATING_NUMERICAL: name = 'local ratings numerical'
elif service_type == HC.BOORU: name = 'booru'
self._listbook.AddPage( listbook, name )
@ -2237,6 +2238,15 @@ class FrameReviewServices( ClientGUICommon.Frame ):
def InitialiseControls():
if service_type in HC.CLIENT_SERVICES:
# show bandwidth used and so on
# if a booru, show how many shares currently active
pass
if service_type in HC.RESTRICTED_SERVICES:
self._permissions_panel = ClientGUICommon.StaticBox( self, 'service permissions' )
@ -2330,6 +2340,15 @@ class FrameReviewServices( ClientGUICommon.Frame ):
vbox = wx.BoxSizer( wx.VERTICAL )
if service_type in HC.CLIENT_SERVICES:
# show bandwidth used and so on
# if a booru, show how many shares currently active
pass
if service_type in HC.RESTRICTED_SERVICES:
self._permissions_panel.AddF( self._account_type, FLAGS_EXPAND_PERPENDICULAR )
@ -2441,6 +2460,15 @@ class FrameReviewServices( ClientGUICommon.Frame ):
now = HC.GetNow()
if service_type in HC.CLIENT_SERVICES:
# show bandwidth used and so on
# if a booru, show how many shares currently active
pass
if service_type in HC.RESTRICTED_SERVICES:
account = self._service.GetAccount()
@ -2536,6 +2564,15 @@ class FrameReviewServices( ClientGUICommon.Frame ):
self._DisplayAccountInfo()
if service_type in HC.CLIENT_SERVICES:
# show bandwidth used and so on
# if a booru, show how many shares currently active
pass
if service_type in [ HC.FILE_REPOSITORY, HC.TAG_REPOSITORY, HC.LOCAL_FILE, HC.LOCAL_TAG, HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ]:
service_info = HC.app.Read( 'service_info', self._service_identifier )

View File

@ -2392,6 +2392,8 @@ class PopupDismissAll( PopupWindow ):
self.SetSizer( hbox )
def Dismiss( self ): pass
def EventButton( self, event ): self.GetParent().DismissAll()
def SetNumMessages( self, num_messages_pending ): self._text.SetLabel( HC.ConvertIntToPrettyString( num_messages_pending ) + ' more messages' )

View File

@ -4517,6 +4517,8 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventPageChanging, source = self._listbook )
# boorus
self._local_ratings_like = ClientGUICommon.ListBook( self._listbook )
self._local_ratings_like.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventServiceChanging )

View File

@ -64,7 +64,7 @@ options = {}
# Misc
NETWORK_VERSION = 13
SOFTWARE_VERSION = 113
SOFTWARE_VERSION = 114
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@ -166,6 +166,7 @@ RATING_NUMERICAL_REPOSITORY = 8
RATING_LIKE_REPOSITORY = 9
COMBINED_TAG = 10
COMBINED_FILE = 11
BOORU = 12
SERVER_ADMIN = 99
NULL_SERVICE = 100
@ -182,6 +183,7 @@ service_string_lookup[ RATING_NUMERICAL_REPOSITORY ] = 'hydrus numerical rating
service_string_lookup[ RATING_LIKE_REPOSITORY ] = 'hydrus like/dislike rating repository'
service_string_lookup[ COMBINED_TAG ] = 'virtual combined tag service'
service_string_lookup[ COMBINED_FILE ] = 'virtual combined file service'
service_string_lookup[ BOORU ] = 'hydrus booru'
service_string_lookup[ SERVER_ADMIN ] = 'hydrus server administration'
service_string_lookup[ NULL_SERVICE ] = 'null service'
@ -189,6 +191,7 @@ RATINGS_SERVICES = [ LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL, RATING_LIKE_REPO
REPOSITORIES = [ TAG_REPOSITORY, FILE_REPOSITORY, RATING_LIKE_REPOSITORY, RATING_NUMERICAL_REPOSITORY ]
RESTRICTED_SERVICES = ( REPOSITORIES ) + [ SERVER_ADMIN, MESSAGE_DEPOT ]
REMOTE_SERVICES = list( RESTRICTED_SERVICES )
CLIENT_SERVICES = [ BOORU ]
ALL_SERVICES = list( REMOTE_SERVICES ) + [ LOCAL_FILE, LOCAL_TAG, LOCAL_RATING_LIKE, LOCAL_RATING_NUMERICAL ]
SERVICES_WITH_THUMBNAILS = [ FILE_REPOSITORY, LOCAL_FILE ]

View File

@ -65,7 +65,7 @@ def GetFileInfo( path, hash ):
elif mime in ( HC.VIDEO_MKV, HC.VIDEO_WEBM ):
( ( width, height ), duration, num_frames ) = HydrusVideoHandling.GetMatroskaOrWebMProperties( path )
( ( width, height ), duration, num_frames ) = HydrusVideoHandling.GetCVVideoProperties( path )
elif mime == HC.APPLICATION_PDF: num_words = HydrusDocumentHandling.GetPDFNumWords( path )
elif mime == HC.AUDIO_MP3: duration = HydrusAudioHandling.GetMP3Duration( path )

View File

@ -248,6 +248,34 @@ def GenerateThumbnail( path, dimensions = HC.UNSCALED_THUMBNAIL_DIMENSIONS ):
return thumbnail
def GetFrameDurations( path ):
pil_image_for_duration = GeneratePILImage( path )
frame_durations = []
i = 0
while True:
try: pil_image_for_duration.seek( i )
except: break
if 'duration' not in pil_image_for_duration.info: duration = 40 # 25 fps default when duration is missing or too funky to extract. most stuff looks ok at this.
else:
duration = pil_image_for_duration.info[ 'duration' ]
if duration == 0: duration = 40
frame_durations.append( duration )
i += 1
return frame_durations
def GetHammingDistance( phash1, phash2 ):
distance = 0
@ -306,10 +334,12 @@ class AnimatedFrameRenderer( FrameRenderer ):
# this code initially written by @fluffy_cub
frame_durations = GetFrameDurations( self._path )
cv_image = cv2.VideoCapture( self._path )
cv_image.set( cv2.cv.CV_CAP_PROP_CONVERT_RGB, True )
no_frames_yet = False
no_frames_yet = True
while True:
@ -322,13 +352,16 @@ class AnimatedFrameRenderer( FrameRenderer ):
else:
no_frames_yet = False
rgb_data = cv2.cvtColor( frame, cv2.COLOR_BGR2RGBA )
pil_frame = PILImage.fromarray( rgb_data, 'RGBA' )
pil_frame = EfficientlyResizeImage( pil_frame, self._target_resolution )
duration = 40 # will have to use pil to get accurate duration, as cv assumes 25fps
try: duration = frame_durations.pop( 0 )
except: duration = 40
yield ( GenerateHydrusBitmapFromPILImage( pil_frame ), duration )
@ -374,9 +407,8 @@ class AnimatedFrameRenderer( FrameRenderer ):
def GetFrames( self ):
for ( frame, duration ) in self._GetFramesPIL(): yield ( frame, duration )
#for ( frame, duration ) in self._GetFramesPIL(): yield ( frame, duration )
'''
try:
for ( frame, duration ) in self._GetFramesCV(): yield ( frame, duration )
@ -385,7 +417,7 @@ class AnimatedFrameRenderer( FrameRenderer ):
for ( frame, duration ) in self._GetFramesPIL(): yield ( frame, duration )
'''
def Render( self ):

View File

@ -1,5 +1,5 @@
import numpy.core.multiarray # important this comes before cv!
import cv
#import numpy.core.multiarray # important this comes before cv!
import cv2
from flvlib import tags as flv_tags
import HydrusConstants as HC
import matroska
@ -47,11 +47,11 @@ def GetFLVProperties( path ):
def GetCVVideoProperties( path ):
cvcapture = cv.CaptureFromFile( path )
capture = cv2.VideoCapture( path )
num_frames = int( cv.GetCaptureProperty( cvcapture, cv.CV_CAP_PROP_FRAME_COUNT ) )
num_frames = int( capture.get( cv2.cv.CV_CAP_PROP_FRAME_COUNT ) )
fps = cv.GetCaptureProperty( cvcapture, cv.CV_CAP_PROP_FPS )
fps = capture.get( cv2.cv.CV_CAP_PROP_FPS )
length_in_seconds = num_frames / fps
@ -59,9 +59,9 @@ def GetCVVideoProperties( path ):
duration = length_in_ms
width = int( cv.GetCaptureProperty( cvcapture, cv.CV_CAP_PROP_FRAME_WIDTH ) )
width = int( capture.get( cv2.cv.CV_CAP_PROP_FRAME_WIDTH ) )
height = int( cv.GetCaptureProperty( cvcapture, cv.CV_CAP_PROP_FRAME_HEIGHT ) )
height = int( capture.get( cv2.cv.CV_CAP_PROP_FRAME_HEIGHT ) )
return ( ( width, height ), duration, num_frames )

View File

@ -0,0 +1,16 @@
import collections
import HydrusConstants as HC
import HydrusImageHandling
import os
import TestConstants
import unittest
import wx
class TestImageHandling( unittest.TestCase ):
def test_phash( self ):
phash = HydrusImageHandling.GeneratePerceptualHash( HC.STATIC_DIR + os.path.sep + 'hydrus.png' )
self.assertEqual( phash, 'a2088220080a2808'.decode( 'hex' ) )

View File

@ -12,6 +12,7 @@ from include import TestDB
from include import TestFunctions
from include import TestHydrusDownloading
from include import TestHydrusEncryption
from include import TestHydrusImageHandling
from include import TestHydrusNATPunch
from include import TestHydrusServer
from include import TestHydrusSessions
@ -79,9 +80,10 @@ class App( wx.App ):
if run_all or only_run == 'daemons': suites.append( unittest.TestLoader().loadTestsFromModule( TestClientDaemons ) )
if run_all or only_run == 'dialogs': suites.append( unittest.TestLoader().loadTestsFromModule( TestDialogs ) )
if run_all or only_run == 'db': suites.append( unittest.TestLoader().loadTestsFromModule( TestDB ) )
if run_all or only_run == 'downloading': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusDownloading ) )
if run_all or only_run == 'encryption': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusEncryption ) )
if run_all or only_run == 'functions': suites.append( unittest.TestLoader().loadTestsFromModule( TestFunctions ) )
if run_all or only_run == 'downloading': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusDownloading ) )
if run_all or only_run == 'image': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusImageHandling ) )
if run_all or only_run == 'nat': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusNATPunch ) )
if run_all or only_run == 'server': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusServer ) )
if run_all or only_run == 'sessions': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusSessions ) )