music_player_flet

Форк
0
/
music_player.py 
482 строки · 18.9 Кб
1
import flet as ft
2
import os
3
import shutil
4
from tinytag import TinyTag
5
from math import pi
6
import asyncio
7

8
class MusicPlayer:
9
    def __init__(self, page):
10
        self.page = page
11
        self.index = 0
12
        self.state = ""
13
        self.audio_controls = []
14
        self.tracks_list = [
15
            "sounds/outfoxing.mp3",
16
            "sounds/param_viper.mp3",
17
            "sounds/track_drums.mp3",
18
        ]
19
        self.volume = 0.5
20
        self.sounds_folder = 'sounds'
21
        if not os.path.exists(self.sounds_folder):
22
            os.makedirs(self.sounds_folder)
23

24
        # Определение стилей
25
        self.styles = {
26
            "default": {
27
                "bgcolor": ft.colors.BLUE_700,
28
                "text_color": ft.colors.BLUE_500,
29
                "button_color": ft.colors.BLUE_400,
30
                "volume_icon": ft.icons.VOLUME_UP,
31
                "volume_slider": ft.colors.BLUE_400
32
            },
33
            "dark": {
34
                "bgcolor": ft.colors.GREY_700,
35
                "text_color": ft.colors.GREY_500,
36
                "button_color": ft.colors.GREY_400,
37
                "volume_icon": ft.icons.VOLUME_UP,
38
                "volume_slider": ft.colors.GREY_400
39
            },
40
            "light": {
41
                "bgcolor": ft.colors.GREEN_700,
42
                "text_color": ft.colors.GREEN_500,
43
                "button_color": ft.colors.GREEN_400,
44
                "volume_icon": ft.icons.VOLUME_UP,
45
                "volume_slider": ft.colors.GREEN_400
46
            },
47
            "sunset_dreams": {
48
                "bgcolor": ft.colors.AMBER_500,
49
                "text_color": ft.colors.AMBER_500,
50
                "button_color": ft.colors.ORANGE_300,
51
                "volume_icon": ft.icons.VOLUME_UP,
52
                "volume_slider": ft.colors.ORANGE_300
53
            },
54
            "ocean_breeze": {
55
                "bgcolor": ft.colors.LIGHT_BLUE_500,
56
                "text_color": ft.colors.LIGHT_BLUE_500,
57
                "button_color": ft.colors.TEAL_400,
58
                "volume_icon": ft.icons.VOLUME_UP,
59
                "volume_slider": ft.colors.TEAL_400
60
            },
61
            "midnight_blue": {
62
                "bgcolor": ft.colors.BLACK,
63
                "text_color": ft.colors.BLACK,
64
                "button_color": ft.colors.BLUE_300,
65
                "volume_icon": ft.icons.VOLUME_UP,
66
                "volume_slider": ft.colors.BLUE_300
67
            },
68
            "forest_green": {
69
                "bgcolor": ft.colors.GREEN_500,
70
                "text_color": ft.colors.GREEN_500,
71
                "button_color": ft.colors.LIGHT_GREEN_300,
72
                "volume_icon": ft.icons.VOLUME_UP,
73
                "volume_slider": ft.colors.LIGHT_GREEN_300
74
            },
75
            "retro_vibes": {
76
                "bgcolor": ft.colors.BROWN_900,
77
                "text_color": ft.colors.ORANGE_200,
78
                "button_color": ft.colors.BROWN_700,
79
                "volume_icon": ft.icons.VOLUME_UP,
80
                "volume_slider": ft.colors.ORANGE_200
81
            },
82
            "neon_lights": {
83
                "bgcolor": ft.colors.BLACK,
84
                "text_color": ft.colors.CYAN_ACCENT_700,
85
                "button_color": ft.colors.GREEN_ACCENT_400,
86
                "volume_icon": ft.icons.VOLUME_UP,
87
                "volume_slider": ft.colors.CYAN_ACCENT_700
88
            },
89
            "pastel_harmony": {
90
                "bgcolor": ft.colors.WHITE,
91
                "text_color": ft.colors.PINK_400,
92
                "button_color": ft.colors.LIGHT_GREEN_300,
93
                "volume_icon": ft.icons.VOLUME_UP,
94
                "volume_slider": ft.colors.LIGHT_GREEN_300
95
            },
96
            "mountain_hike": {
97
                "bgcolor": ft.colors.GREY_900,
98
                "text_color": ft.colors.BROWN_400,
99
                "button_color": ft.colors.BROWN_600,
100
                "volume_icon": ft.icons.VOLUME_UP,
101
                "volume_slider": ft.colors.BROWN_600
102
            },
103
            "vibrant_citrus": {
104
                "bgcolor": ft.colors.YELLOW_500,
105
                "text_color": ft.colors.BLACK,
106
                "button_color": ft.colors.ORANGE_400,
107
                "volume_icon": ft.icons.VOLUME_UP,
108
                "volume_slider": ft.colors.ORANGE_400
109
            }
110
        }
111
        self.current_style = "default"
112

113
    def change_style(self, e):
114
        styles_list = list(self.styles.keys())
115
        current_index = styles_list.index(self.current_style)
116
        self.current_style = styles_list[(current_index + 1) % len(styles_list)]
117
        self.update_style()
118
        self.page.update()
119

120
    def update_style(self):
121
        style = self.styles[self.current_style]
122
        self.page.bgcolor = style["bgcolor"]  # Установка цвета фона страницы
123
        self.current_time.color = style["text_color"]
124
        self.remaining_time.color = style["text_color"]
125
        self.track_name.color = style["text_color"]
126
        self.track_artist.color = style["text_color"]
127
        self.progress_bar.bgcolor = style["button_color"]
128
        self.volume_icon.name = style["volume_icon"]
129
        self.volume_slider.active_color = style["volume_slider"]
130

131
    def pick_files_result(self, e: ft.FilePickerResultEvent):
132
        if e.files:
133
            for f in e.files:
134
                try:
135
                    src = f.path.replace("\\", "/")
136
                    filename = os.path.basename(src)
137
                    dest = os.path.join(self.sounds_folder, filename)
138
                    shutil.copy(src, dest)
139
                    self.tracks_list.append(dest)
140

141
                    audio1 = ft.Audio(
142
                        src=dest,
143
                        autoplay=False,
144
                        volume=self.volume,
145
                        balance=0,
146
                        on_position_changed=self.progress_change,
147
                        on_state_changed=self.check_state,
148
                    )
149
                    self.audio_controls.append(audio1)
150
                    self.page.overlay.append(audio1)
151

152
                    audio_info = TinyTag.get(dest)
153
                    self.track_name.value = audio_info.title if audio_info.title else "Неизвестное название"
154
                    self.track_artist.value = audio_info.artist if audio_info.artist else "Неизвестный исполнитель"
155

156
                except Exception as ex:
157
                    print(f"Ошибка при обработке файла {f.path}: {ex}")
158

159
            self.update_track_list()
160
            self.page.update()
161
        else:
162
            print("Отмена")
163

164
    def check_state(self, e):
165
        if e.data == "completed":
166
            self.next_track(None)
167
        self.page.update()
168

169
    def play_track(self, e):
170
        if not self.audio_controls:
171
            print("Нет доступных треков для воспроизведения")
172
            return
173

174
        if self.state == "":
175
            self.state = "playing"
176
            self.btn_play.icon = ft.icons.PAUSE_CIRCLE
177
            self.audio_controls[self.index].play()
178
        elif self.state == "playing":
179
            self.state = "paused"
180
            self.btn_play.icon = ft.icons.PLAY_CIRCLE
181
            self.audio_controls[self.index].pause()
182
        else:
183
            self.state = "playing"
184
            self.btn_play.icon = ft.icons.PAUSE_CIRCLE
185
            self.audio_controls[self.index].resume()
186
        self.page.update()
187

188
    def new_track(self):
189
        self.disc_image.rotate.angle += pi * 2
190
        audio = TinyTag.get(self.audio_controls[self.index].src)
191
        self.track_name.value = audio.title if audio.title else "Неизвестное название"
192
        self.track_artist.value = audio.artist if audio.artist else "Неизвестный исполнитель"
193
        self.current_time.value = "0:0"
194
        self.remaining_time.value = self.converter_time(audio.duration * 1000)
195
        self.progress_track.value = 0.0
196

197
        for audio_control in self.audio_controls:
198
            audio_control.pause()
199

200
        if self.state == "playing":
201
            self.audio_controls[self.index].volume = self.volume
202
            self.audio_controls[self.index].play()
203

204
        self.page.update()
205

206
    def next_track(self, e):
207
        if not self.audio_controls:
208
            print("Нет доступных треков для воспроизведения")
209
            return
210

211
        self.audio_controls[self.index].pause()
212
        self.index = (self.index + 1) % len(self.audio_controls)
213
        self.new_track()
214
        self.page.update()
215

216
    def previous_track(self, e):
217
        if not self.audio_controls:
218
            print("Нет доступных треков для воспроизведения")
219
            return
220

221
        self.audio_controls[self.index].pause()
222
        self.index = (self.index - 1) % len(self.audio_controls)
223
        self.new_track()
224
        self.page.update()
225

226
    def seek_forward(self, e):
227
        if not self.audio_controls:
228
            print("Нет доступных треков для воспроизведения")
229
            return
230

231
        current_position = self.audio_controls[self.index].position
232
        new_position = current_position + 10000
233
        if new_position > self.audio_controls[self.index].duration * 1000:
234
            new_position = self.audio_controls[self.index].duration * 1000
235
        print(f"Seeking forward: Current Position: {current_position}, New Position: {new_position}")
236
        self.audio_controls[self.index].position = new_position
237
        self.progress_change(ft.Event(data=new_position))
238
        self.page.update()
239

240
    def seek_backward(self, e):
241
        if not self.audio_controls:
242
            print("Нет доступных треков для воспроизведения")
243
            return
244

245
        current_position = self.audio_controls[self.index].position
246
        new_position = current_position - 10000
247
        if new_position < 0:
248
            new_position = 0
249
        print(f"Seeking backward: Current Position: {current_position}, New Position: {new_position}")
250
        self.audio_controls[self.index].position = new_position
251
        self.progress_change(ft.Event(data=new_position))
252
        self.page.update()
253

254
    def volume_change(self, e):
255
        if not self.audio_controls:
256
            print("Нет доступных треков для воспроизведения")
257
            return
258

259
        v = e.control.value
260
        self.audio_controls[self.index].volume = 0.01 * v
261
        self.volume = 0.01 * v
262
        if v == 0:
263
            self.volume_icon.name = ft.icons.VOLUME_OFF
264
        elif 0 < v <= 50:
265
            self.volume_icon.name = ft.icons.VOLUME_DOWN
266
        else:
267
            self.volume_icon.name = ft.icons.VOLUME_UP
268
        self.page.update()
269

270
    @staticmethod
271
    def converter_time(millis):
272
        millis = int(millis)
273
        seconds = (millis // 1000) % 60
274
        minutes = (millis // (1000 * 60)) % 60
275
        return f"{int(minutes)}:{int(seconds):02d}"
276

277
    def progress_change(self, e):
278
        if not self.audio_controls:
279
            print("Нет доступных треков для воспроизведения")
280
            return
281

282
        audio = TinyTag.get(self.audio_controls[self.index].src)
283
        self.current_time.value = self.converter_time(e.data)
284
        self.remaining_time.value = self.converter_time((audio.duration * 1000) - int(e.data))
285
        self.progress_track.value = float(e.data) / (audio.duration * 1000)
286
        self.page.update()
287

288
    def update_track_list(self):
289
        # Обновите элемент интерфейса списка треков, если у вас есть такой элемент
290
        pass
291

292
    def on_progress_bar_click(self, e):
293
        if not self.audio_controls:
294
            print("Нет доступных треков для воспроизведения")
295
            return
296

297
        print(f"Click event data: {e.data}")
298
        if e.data and 'x' in e.data:
299
            click_position = e.data['x']
300
            print(f"Progress bar clicked at: {click_position}")
301

302
            progress_width = self.progress_bar.width
303
            print(f"Progress bar width: {progress_width}")
304

305
            if 0 <= click_position <= progress_width:
306
                new_position = (click_position / progress_width) * (self.audio_controls[self.index].duration * 1000)
307
                print(f"Setting new position: {new_position}")
308
                self.audio_controls[self.index].position = new_position
309
                self.progress_change(ft.Event(data=new_position))
310
                self.page.update()
311

312
    def build_ui(self):
313
        self.pick_files_dialog = ft.FilePicker(on_result=self.pick_files_result)
314
        self.upload_button = ft.IconButton(
315
            icon=ft.icons.UPLOAD_FILE,
316
            icon_size=35,
317
            on_click=lambda _: self.pick_files_dialog.pick_files(
318
                allow_multiple=True,
319
                file_type=ft.FilePickerFileType.AUDIO
320
            )
321
        )
322
        self.style_button = ft.IconButton(
323
            icon=ft.icons.STYLE,
324
            icon_size=35,
325
            on_click=self.change_style
326
        )
327

328
        for track in self.tracks_list:
329
            audio = ft.Audio(
330
                src=track,
331
                autoplay=False,
332
                volume=self.volume,
333
                balance=0,
334
                on_position_changed=self.progress_change,
335
                on_state_changed=self.check_state,
336
            )
337
            self.audio_controls.append(audio)
338
            self.page.overlay.append(audio)
339

340
        audio_init = TinyTag.get(self.audio_controls[self.index].src)
341

342
        self.current_time = ft.Text(value="0:0")
343
        self.remaining_time = ft.Text(value=self.converter_time(audio_init.duration * 1000))
344
        self.progress_bar = ft.Container(
345
            width=400,
346
            height=8,
347
            bgcolor=self.styles[self.current_style]["button_color"],
348
            on_click=self.on_progress_bar_click
349
        )
350
        self.progress_track = ft.Container(
351
            width=400,
352
            height=8,
353
            bgcolor=ft.colors.BLUE,
354
        )
355
        self.track_name = ft.Text(value=audio_init.title if audio_init.title else "Неизвестное название")
356
        self.track_artist = ft.Text(value=audio_init.artist if audio_init.artist else "Неизвестный исполнитель")
357
        self.btn_play = ft.IconButton(icon=ft.icons.PLAY_CIRCLE, icon_size=35, on_click=self.play_track)
358
        self.volume_icon = ft.Icon(name=self.styles[self.current_style]["volume_icon"])
359

360
        # Установите значение слайдера громкости в соответствии с текущей громкостью
361
        self.volume_slider = ft.Slider(
362
            width=150,
363
            active_color=self.styles[self.current_style]["volume_slider"],
364
            min=0,
365
            max=100,
366
            divisions=100,
367
            value=self.volume * 100,
368
            label="{value}",
369
            on_change=self.volume_change,
370
        )
371

372
        self.disc_image = ft.Image(
373
            src="assets/album.png",
374
            width=180,
375
            height=180,
376
            fit=ft.ImageFit.CONTAIN,
377
            rotate=ft.transform.Rotate(0, alignment=ft.alignment.center),
378
            animate_rotation=ft.animation.Animation(300, ft.AnimationCurve.LINEAR),
379
        )
380

381
        main_content = ft.Card(
382
            content=ft.Container(
383
                content=ft.Row(
384
                    [
385
                        ft.Container(width=80, height=300),
386
                        ft.Column(
387
                            [
388
                                ft.ListTile(
389
                                    leading=ft.Icon(ft.icons.MUSIC_NOTE_ROUNDED),
390
                                    title=self.track_name,
391
                                    subtitle=self.track_artist,
392
                                ),
393
                                ft.Row(
394
                                    [self.current_time, self.progress_bar, self.remaining_time],
395
                                    alignment=ft.MainAxisAlignment.END,
396
                                ),
397
                                ft.Row(
398
                                    [
399
                                        ft.IconButton(
400
                                            icon=ft.icons.SKIP_PREVIOUS,
401
                                            icon_size=35,
402
                                            on_click=self.previous_track,
403
                                        ),
404
                                        self.btn_play,
405
                                        ft.IconButton(
406
                                            icon=ft.icons.SKIP_NEXT,
407
                                            icon_size=35,
408
                                            on_click=self.next_track,
409
                                        ),
410
                                        self.volume_icon,
411
                                        self.volume_slider,  # Используйте слайдер громкости
412
                                        self.upload_button,
413
                                        self.style_button,
414
                                    ],
415
                                    alignment=ft.MainAxisAlignment.END,
416
                                ),
417
                            ]
418
                        ),
419
                    ]
420
                ),
421
            ),
422
            right=-10,
423
            width=620,
424
            color=ft.colors.ON_PRIMARY,
425
            height=180,
426
        )
427

428
        stack = ft.Stack(
429
            controls=[
430
                main_content,
431
                self.disc_image,
432
                self.pick_files_dialog,
433
            ],
434
            width=700,
435
            height=300,
436
        )
437

438
        self.page.add(stack)
439

440
async def monitor_new_tracks(player: MusicPlayer):
441
    while True:
442
        files_in_folder = os.listdir(player.sounds_folder)
443
        if files_in_folder:
444
            for filename in files_in_folder:
445
                if not filename.endswith(".mp3"):
446
                    continue
447
                full_path = os.path.join(player.sounds_folder, filename)
448
                if full_path not in player.tracks_list:
449
                    player.tracks_list.append(full_path)
450
                    audio1 = ft.Audio(
451
                        src=full_path,
452
                        autoplay=False,
453
                        volume=player.volume,
454
                        balance=0,
455
                        on_position_changed=player.progress_change,
456
                        on_state_changed=player.check_state,
457
                    )
458
                    player.audio_controls.append(audio1)
459
                    player.page.overlay.append(audio1)
460
                    player.update_track_list()
461
                    player.page.update()
462
        await asyncio.sleep(3)
463

464
async def main(page: ft.Page):
465
    page.title = "Музыкальный плеер"
466
    page.vertical_alignment = ft.MainAxisAlignment.CENTER
467
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
468
    page.window.height = 300
469
    page.window.width = 800
470
    page.window.min_width = 800
471
    page.window.min_height = 300
472

473
    player = MusicPlayer(page)
474
    player.build_ui()
475

476
    asyncio.create_task(monitor_new_tracks(player))
477

478
ft.app(target=main)
479

480
'''
481

482
'''

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.