1
- """
2
- Simplest example.
3
- """
1
+ """Simplest example of files_dropdown_menu + notification."""
4
2
5
- from os import path , environ
3
+ import tempfile
4
+ from os import environ , path
6
5
from typing import Annotated
7
6
8
- import uvicorn
9
- from fastapi import FastAPI , Depends
10
- from requests import Response
11
- import urllib3
12
-
13
- import tempfile
14
- from pygifsicle import optimize
15
- import imageio
16
7
import cv2
8
+ import imageio
17
9
import numpy
10
+ import urllib3
11
+ import uvicorn
12
+ from fastapi import BackgroundTasks , Depends , FastAPI
13
+ from pygifsicle import optimize
14
+ from requests import Response
18
15
19
- from nc_py_api import UiFileActionHandlerInfo , LogLvl , NextcloudApp , nc_app , set_enabled_handler , ApiScope , set_scopes
16
+ from nc_py_api import (
17
+ ApiScope ,
18
+ GuiActionFileInfo ,
19
+ GuiFileActionHandlerInfo ,
20
+ LogLvl ,
21
+ NextcloudApp ,
22
+ enable_heartbeat ,
23
+ nc_app ,
24
+ set_enabled_handler ,
25
+ set_scopes ,
26
+ )
20
27
21
28
APP = FastAPI ()
22
29
23
30
24
- @APP .post ("/video_to_gif" )
25
- async def video_to_gif (
26
- file : UiFileActionHandlerInfo ,
27
- nc : Annotated [NextcloudApp , Depends (nc_app )],
28
- ):
29
- source_path = path .join (file .actionFile .dir , file .actionFile .name )
31
+ def convert_video_to_gif (input_params : GuiActionFileInfo , nc : NextcloudApp ):
32
+ source_path = path .join (input_params .directory , input_params .name )
30
33
save_path = path .splitext (source_path )[0 ] + ".gif"
31
34
nc .log (LogLvl .WARNING , f"Processing:{ source_path } -> { save_path } " )
32
- source_file = nc .files .download (source_path )
33
- nc .log (LogLvl .WARNING , "File downloaded" )
34
35
try :
35
36
with tempfile .NamedTemporaryFile (mode = "w+b" ) as tmp_in :
36
- tmp_in .write (source_file )
37
+ nc .files .download2stream (source_path , tmp_in )
38
+ nc .log (LogLvl .WARNING , "File downloaded" )
37
39
tmp_in .flush ()
38
40
cap = cv2 .VideoCapture (tmp_in .name )
39
41
with tempfile .NamedTemporaryFile (mode = "w+b" , suffix = ".gif" ) as tmp_out :
40
42
image_lst = []
41
43
previous_frame = None
44
+ skip = 0
42
45
while True :
46
+ skip += 1
43
47
ret , frame = cap .read ()
48
+ if frame is None :
49
+ break
50
+ if skip == 2 :
51
+ skip = 0
52
+ continue
44
53
if previous_frame is not None :
45
54
diff = numpy .mean (previous_frame != frame )
46
55
if diff < 0.91 :
47
56
continue
48
- if frame is None :
49
- break
50
57
frame_rgb = cv2 .cvtColor (frame , cv2 .COLOR_BGR2RGB )
51
58
image_lst .append (frame_rgb )
52
59
previous_frame = frame
60
+ if len (image_lst ) > 60 :
61
+ break
53
62
cap .release ()
54
63
imageio .mimsave (tmp_out .name , image_lst )
55
64
optimize (tmp_out .name )
56
65
nc .log (LogLvl .WARNING , "GIF is ready" )
57
- nc .files .upload (save_path , content = tmp_out . read () )
66
+ nc .files .upload_stream (save_path , tmp_out )
58
67
nc .log (LogLvl .WARNING , "Result uploaded" )
68
+ nc .users .notifications .create (f"{ input_params .name } finished!" , f"{ save_path } is waiting for you!" )
59
69
except Exception as e :
60
70
nc .log (LogLvl .ERROR , str (e ))
71
+ nc .users .notifications .create ("Error occurred" , "Error information was written to log file" )
72
+
73
+
74
+ @APP .post ("/video_to_gif" )
75
+ async def video_to_gif (
76
+ file : GuiFileActionHandlerInfo ,
77
+ nc : Annotated [NextcloudApp , Depends (nc_app )],
78
+ background_tasks : BackgroundTasks ,
79
+ ):
80
+ background_tasks .add_task (convert_video_to_gif , file .actionFile , nc )
61
81
return Response ()
62
82
63
83
64
84
def enabled_handler (enabled : bool , nc : NextcloudApp ) -> str :
65
85
print (f"enabled={ enabled } " )
66
86
try :
67
87
if enabled :
68
- nc .ui_files_actions .register ("to_gif" , "TO GIF" , "/video_to_gif" , mime = "video" )
88
+ nc .gui . files_dropdown_menu .register ("to_gif" , "TO GIF" , "/video_to_gif" , mime = "video" )
69
89
else :
70
- nc .ui_files_actions .unregister ("to_gif" )
90
+ nc .gui . files_dropdown_menu .unregister ("to_gif" )
71
91
except Exception as e :
72
92
return str (e )
73
93
return ""
@@ -76,25 +96,10 @@ def enabled_handler(enabled: bool, nc: NextcloudApp) -> str:
76
96
@APP .on_event ("startup" )
77
97
def initialization ():
78
98
set_enabled_handler (APP , enabled_handler )
79
- set_scopes (APP , {
80
- "required" : [ApiScope .DAV ],
81
- "optional" : [ApiScope .NOTIFICATIONS ]
82
- })
99
+ set_scopes (APP , {"required" : [ApiScope .DAV ], "optional" : [ApiScope .NOTIFICATIONS ]})
100
+ enable_heartbeat (APP )
83
101
84
102
85
103
if __name__ == "__main__" :
86
- # This should be set by packaging step
87
- secret = "tC6vkwPhcppjMykD1r0n9NlI95uJMBYjs5blpIcA1PAdoPDmc5qoAjaBAkyocZ6E" \
88
- "X1T8Pi+T5papEolTLxz3fJSPS8ffC4204YmggxPsbJdCkXHWNPHKWS9B+vTj2SIV"
89
- if "app_name" not in environ :
90
- environ ["app_name" ] = "nc_py_api"
91
- if "app_version" not in environ :
92
- environ ["app_version" ] = "1.0.0"
93
- if "app_secret" not in environ :
94
- environ ["app_secret" ] = secret
95
- if "nextcloud_url" not in environ :
96
- environ ["nextcloud_url" ] = "http://nextcloud.local/index.php"
97
- # environ["app_name"] = "test_app"
98
- # ---------
99
104
urllib3 .disable_warnings ()
100
- uvicorn .run ("main:APP" , host = "0 .0.0.0" , port = 9001 , log_level = "trace" , reload = True )
105
+ uvicorn .run ("main:APP" , host = environ . get ( "APP_HOST" , "127 .0.0.1" ) , port = int ( environ [ "APP_PORT" ]) , log_level = "trace" )
0 commit comments