bluenevus commited on
Commit
557bfa3
·
verified ·
1 Parent(s): a57b98f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -0
app.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import io
3
+ import os
4
+ import zipfile
5
+ from threading import Thread
6
+
7
+ import dash
8
+ import dash_bootstrap_components as dbc
9
+ from dash import dcc, html, Input, Output, State, callback
10
+ from dash.exceptions import PreventUpdate
11
+ from PyPDF2 import PdfReader, PdfWriter
12
+
13
+ app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
14
+
15
+ # Global variables
16
+ generated_file = None
17
+ progress = 0
18
+
19
+ # Layout
20
+ app.layout = dbc.Container([
21
+ html.H1("PDF Splitter", className="my-4"),
22
+ dcc.Upload(
23
+ id='upload-pdf',
24
+ children=html.Div(['Drag and Drop or ', html.A('Select PDF')]),
25
+ style={
26
+ 'width': '100%',
27
+ 'height': '60px',
28
+ 'lineHeight': '60px',
29
+ 'borderWidth': '1px',
30
+ 'borderStyle': 'dashed',
31
+ 'borderRadius': '5px',
32
+ 'textAlign': 'center',
33
+ 'margin': '10px'
34
+ },
35
+ multiple=False
36
+ ),
37
+ html.Div(id='pdf-name'),
38
+ dbc.Card([
39
+ dbc.CardBody([
40
+ dbc.Input(id='page-range', type='text', placeholder='Enter page range (e.g., 1-3)'),
41
+ dbc.Button("Add Range", id='add-range', color="secondary", className="mt-2"),
42
+ html.Div(id='ranges-list'),
43
+ ])
44
+ ], className="my-3"),
45
+ dbc.Button("Split PDF", id='split-button', color="primary", className="mt-3", disabled=True),
46
+ dcc.Progress(id='progress-bar', className="my-3"),
47
+ dbc.Button("Download ZIP", id='download-button', color="success", className="mt-3", disabled=True),
48
+ dcc.Download(id="download-zip"),
49
+ ], fluid=True)
50
+
51
+ @callback(
52
+ Output('pdf-name', 'children'),
53
+ Output('split-button', 'disabled'),
54
+ Input('upload-pdf', 'contents'),
55
+ State('upload-pdf', 'filename')
56
+ )
57
+ def update_output(contents, filename):
58
+ if contents is not None:
59
+ return f"Selected file: {filename}", False
60
+ return "No file selected", True
61
+
62
+ @callback(
63
+ Output('ranges-list', 'children'),
64
+ Input('add-range', 'n_clicks'),
65
+ State('page-range', 'value'),
66
+ State('ranges-list', 'children')
67
+ )
68
+ def add_range(n_clicks, new_range, existing_ranges):
69
+ if n_clicks is None or not new_range:
70
+ return existing_ranges or []
71
+ new_item = html.Div(f"Range: {new_range}")
72
+ return existing_ranges + [new_item] if existing_ranges else [new_item]
73
+
74
+ def process_pdf(contents, filename, ranges):
75
+ global generated_file, progress
76
+ progress = 0
77
+
78
+ try:
79
+ content_type, content_string = contents.split(',')
80
+ decoded = base64.b64decode(content_string)
81
+ pdf = PdfReader(io.BytesIO(decoded))
82
+
83
+ writers = []
84
+ for range_str in ranges:
85
+ start, end = map(int, range_str.split('-'))
86
+ writer = PdfWriter()
87
+ for i in range(start - 1, min(end, len(pdf.pages))):
88
+ writer.add_page(pdf.pages[i])
89
+ writers.append(writer)
90
+
91
+ zip_buffer = io.BytesIO()
92
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
93
+ for i, writer in enumerate(writers):
94
+ progress = (i + 1) / len(writers) * 100
95
+ pdf_buffer = io.BytesIO()
96
+ writer.write(pdf_buffer)
97
+ pdf_buffer.seek(0)
98
+ zf.writestr(f"split_{i+1}.pdf", pdf_buffer.getvalue())
99
+
100
+ zip_buffer.seek(0)
101
+ generated_file = zip_buffer.getvalue()
102
+ progress = 100
103
+ except Exception as e:
104
+ print(f"Error processing PDF: {str(e)}")
105
+ progress = -1
106
+
107
+ @callback(
108
+ Output('progress-bar', 'value'),
109
+ Output('download-button', 'disabled'),
110
+ Input('split-button', 'n_clicks'),
111
+ State('upload-pdf', 'contents'),
112
+ State('upload-pdf', 'filename'),
113
+ State('ranges-list', 'children'),
114
+ prevent_initial_call=True
115
+ )
116
+ def split_pdf(n_clicks, contents, filename, ranges):
117
+ if not contents or not ranges:
118
+ raise PreventUpdate
119
+
120
+ ranges = [r['props']['children'].split(': ')[1] for r in ranges]
121
+ thread = Thread(target=process_pdf, args=(contents, filename, ranges))
122
+ thread.start()
123
+
124
+ return 0, True
125
+
126
+ @callback(
127
+ Output('progress-bar', 'value', allow_duplicate=True),
128
+ Output('download-button', 'disabled', allow_duplicate=True),
129
+ Input('progress-bar', 'value'),
130
+ prevent_initial_call=True
131
+ )
132
+ def update_progress(value):
133
+ global progress
134
+ if progress == 100:
135
+ return 100, False
136
+ elif progress == -1:
137
+ return 0, True
138
+ else:
139
+ return progress, True
140
+
141
+ @callback(
142
+ Output("download-zip", "data"),
143
+ Input("download-button", "n_clicks"),
144
+ prevent_initial_call=True
145
+ )
146
+ def download_zip(n_clicks):
147
+ global generated_file
148
+ if generated_file is not None:
149
+ return dcc.send_bytes(generated_file, "split_pdfs.zip")
150
+ raise PreventUpdate
151
+
152
+ if __name__ == '__main__':
153
+ print("Starting the Dash application...")
154
+ app.run(debug=True, host='0.0.0.0', port=7860)
155
+ print("Dash application has finished running.")