Compare commits

...

285 Commits
v0.5 ... master

Author SHA1 Message Date
7d340bb7eb
Readded cx_freeze as nuitka was unable to build yt-dlp 2022-10-21 12:55:13 -05:00
fb2395ce67
disabled VK downloader temporarily [Skip CI] 2022-10-21 09:39:57 -05:00
2386401633
Added mpv-2.dll for both supported platforms 2022-10-20 17:29:45 -05:00
aab1230c07
Replaced searches and downloads for yt-dlp implementation [Skip CI] 2022-10-20 17:26:18 -05:00
516e20c3b8
Changes on GUI for new player module 2022-10-20 17:25:29 -05:00
062bbcc842
Start rewrite of player control module with mpv [Skip CI] 2022-10-20 17:25:05 -05:00
f5ecaefe8c
Removed sound_lib on favour of mpv; replaced youtube_dl for yt-dlp 2022-10-20 17:23:59 -05:00
3b17f4b7b0
Updated move command 2022-10-19 17:32:44 -05:00
8aa55b0aa4
Updated python version and wx version for building the app 2022-10-19 17:17:35 -05:00
2613803031
Ask Youtube to retrieve json results 2022-03-25 13:52:57 -06:00
b286e526cc
Generate version at build time 2022-03-25 13:26:44 -06:00
a1b29005bb
Fixed version generate when checking updates 2022-03-25 13:09:26 -06:00
6eaa0f50a1
Generate update files with right version 2022-03-25 12:58:13 -06:00
0ea41cea89
Fixed links for the updates file 2021-12-30 19:41:40 -06:00
5c9169fadd
Fixed install dir for 64 bits executable 2021-12-30 19:39:21 -06:00
817df4219b
Move installer file to artifacts on first build fase for the 64 bits app 2021-12-30 19:22:29 -06:00
70273b2023
Call installer.nsi by using the right path 2021-12-30 19:08:53 -06:00
9bd3b7f448
Fix artifacts creation; try to delete no longer files on last step before upload 2021-12-30 19:01:18 -06:00
0fef6427ff
Try to generate an installer and a 64 bits version 2021-12-30 18:50:42 -06:00
32fb52f941
Updated copyright sections 2021-12-30 18:49:01 -06:00
ef36948ab8
Updated URL to retrieve updates to our new gitlab repository 2021-12-30 18:48:47 -06:00
236539f337
Moved project to our own runner 2021-12-29 10:51:40 -06:00
af16329c72 Added windows dependencies for 32 and 64 bits 2021-09-24 16:13:38 -05:00
ebb400672f Fixed player function on end playback 2021-09-24 13:19:30 -05:00
9bb85688bb Modified setup to remove LibVLc inclussion 2021-09-24 10:18:17 -05:00
da4433894f Removed libVLc dependencies 2021-09-23 16:57:50 -05:00
b60e62a56d Fixed function to get the next song automatically 2021-09-23 16:57:10 -05:00
74d9b5b1a4 Fixed playback status representation in slider controls 2021-09-23 16:55:48 -05:00
14d311f29d Fixed tidal authentication. Now it requires to authorize accounts via a browser 2021-09-23 16:12:03 -05:00
441277a472
fixed a couple types in str definition fo config files 2021-09-22 23:39:26 -05:00
b0f241b2b1 Implemented most of the code for Tidal updates. Only authorization is pending for now 2021-09-22 17:51:43 -05:00
7d64515bff Get and set default devices from sound_lib 2021-09-22 17:33:59 -05:00
cc1216f7fe Updated config spec 2021-09-22 17:32:27 -05:00
226a17b4fe Changed VLC again for sound_lib 2021-09-22 17:32:10 -05:00
1cd7d2ad39 Removed some old code no longer needed 2021-09-22 17:25:22 -05:00
c5d103dc8d disabled automatic generation of new versions on every commit to master for a while 2021-09-22 17:24:31 -05:00
1a5fe83eee Install tidalapi from pip; added sound_lib as a dependency; remove libVLC module from requirements file 2021-09-22 17:21:03 -05:00
ab264d4100 changed indent style 2021-09-22 15:32:52 -05:00
de28e80327 Updated dependencies to match changes in git repositories 2021-09-22 15:30:14 -05:00
ea01e13930 Remove tests for a while 2021-01-19 16:19:43 -06:00
2ef50b73c4 Call to update generator before uploading artifacts 2021-01-19 16:19:14 -06:00
18fd1610bf Changed more files to adapt to the new hosting provider 2021-01-19 15:57:27 -06:00
b498dfb91e Fixed requirements 2021-01-19 15:37:43 -06:00
5f20f2ea91 Updated CI configuration to build with Gitlab runners 2021-01-19 15:32:40 -06:00
0e63c3e148 Modified script to upload to an insecure ftp folder 2021-01-19 15:32:23 -06:00
ee1e13deac Updated locales [skip ci] 2020-12-29 05:08:21 -08:00
1fd75dfa93 Attempt to build MusicDL against Python 3.9.1 2020-12-29 06:57:27 -06:00
52dd577b6c Updated changelog 2020-12-29 06:57:08 -06:00
7cb6a10db4 Updated locales [skip ci] 2020-12-28 21:22:19 -08:00
a09079ba43 Merge branch 'master' of code.manuelcortez.net:manuelcortez/music-dl 2020-12-28 23:09:11 -06:00
c4694ac4b1 Updated VLC components to Version 3.0.11. 2020-12-28 23:08:41 -06:00
a068ea2342 Disabled zaicev.net by default 2020-12-26 22:30:15 -06:00
ba39db0e46 Updated locales [skip ci] 2020-12-26 20:20:56 -08:00
e321cdf259 Generates the po files after everything else 2020-12-26 22:09:37 -06:00
b9707aaf18 Updated locales [skip ci] 2020-12-26 19:56:46 -08:00
4f7b9c5b60 Merge branch 'master' of code.manuelcortez.net:manuelcortez/music-dl 2020-12-26 21:54:19 -06:00
162ce4491b Updated gitlab CI file to avoid duplicating a CI job 2020-12-26 21:54:00 -06:00
b36504a493 Updated locales [skip ci] 2020-12-26 16:25:43 -08:00
fe878b227d Updated locales [skip ci] 2020-12-25 16:25:26 -08:00
3fe5d98f66 Updated locales [skip ci] 2020-12-24 16:25:28 -08:00
eb61d55d20 Updated locales [skip ci] 2020-12-23 16:27:20 -08:00
346f6e15fe Updated locales [skip ci] 2020-12-22 16:26:12 -08:00
52be306691 Updated locales [skip ci] 2020-12-21 16:27:33 -08:00
282b840c02 Updated locales [skip ci] 2020-12-20 16:26:53 -08:00
d300713f94 Updated locales [skip ci] 2020-12-19 16:27:07 -08:00
b8c55af897 Updated locales [skip ci] 2020-12-18 16:26:29 -08:00
643f9c5613 Updated locales [skip ci] 2020-12-17 16:26:36 -08:00
475e9fd573 Updated locales [skip ci] 2020-12-16 16:26:52 -08:00
f6b08cb046 Updated locales [skip ci] 2020-12-15 16:25:44 -08:00
c1931f22bc Updated locales [skip ci] 2020-12-14 16:25:53 -08:00
12c3da2886 Updated locales [skip ci] 2020-12-13 16:26:00 -08:00
79a7de3940 Updated locales [skip ci] 2020-12-12 16:25:23 -08:00
91db712833 Updated locales [skip ci] 2020-12-11 16:25:53 -08:00
de86808eb4 Updated locales [skip ci] 2020-12-10 16:25:59 -08:00
d7f7e508a3 Updated locales [skip ci] 2020-12-09 16:26:07 -08:00
55eb7244a3 Updated locales [skip ci] 2020-12-08 16:25:37 -08:00
2aa39da0d8 Updated locales [skip ci] 2020-12-07 16:25:47 -08:00
e4356d58e0 Updated locales [skip ci] 2020-12-06 16:25:44 -08:00
cc8ea272b9 Updated locales [skip ci] 2020-12-05 16:24:54 -08:00
360f4a7ce4 Updated locales [skip ci] 2020-12-04 16:24:26 -08:00
2d329ac8e9 Updated locales [skip ci] 2020-12-03 16:24:43 -08:00
0d1172dd9e Updated locales [skip ci] 2020-12-02 16:24:45 -08:00
ff17f36781 Updated locales [skip ci] 2020-12-01 16:24:34 -08:00
7087edc1a5 Updated locales [skip ci] 2020-11-30 16:24:29 -08:00
76d62eaca2 Updated locales [skip ci] 2020-11-29 16:24:18 -08:00
1d236d6e0f Updated locales [skip ci] 2020-11-28 16:25:00 -08:00
90b155c04a Updated locales [skip ci] 2020-11-27 16:23:49 -08:00
fb4b0b835e Updated locales [skip ci] 2020-11-26 16:24:29 -08:00
dfdd3785ef Updated locales [skip ci] 2020-11-25 16:24:22 -08:00
8d92b01fa4 Updated locales [skip ci] 2020-11-24 16:24:16 -08:00
e9626b977e Updated locales [skip ci] 2020-11-23 16:24:36 -08:00
68798328d2 Updated locales [skip ci] 2020-11-22 16:24:13 -08:00
908eb64481 Updated locales [skip ci] 2020-11-21 16:24:09 -08:00
cac07c0864 Updated locales [skip ci] 2020-11-20 16:23:49 -08:00
27d593b512 Updated locales [skip ci] 2020-11-19 16:23:57 -08:00
0ead659156 Updated locales [skip ci] 2020-11-18 16:26:36 -08:00
c66c924321 Updated locales [skip ci] 2020-11-17 16:27:42 -08:00
e985f97811 Updated locales [skip ci] 2020-11-16 16:27:55 -08:00
34bee5ad50 Updated locales [skip ci] 2020-11-15 16:28:03 -08:00
d62c4e226d Updated locales [skip ci] 2020-11-14 16:25:52 -08:00
36eb2b2537 Updated locales [skip ci] 2020-11-13 16:27:26 -08:00
98184a9abc Updated locales [skip ci] 2020-11-12 16:26:47 -08:00
4a625b7a29 Updated locales [skip ci] 2020-11-11 16:28:43 -08:00
241a553fce Updated locales [skip ci] 2020-11-10 16:24:22 -08:00
da0bb3a6aa Updated locales [skip ci] 2020-11-09 16:23:45 -08:00
afffd9ba15 Updated locales [skip ci] 2020-11-08 16:24:37 -08:00
4d4e0bf283 Updated locales [skip ci] 2020-11-07 16:24:46 -08:00
6429e80964 Updated locales [skip ci] 2020-11-06 16:24:26 -08:00
2579224d80 Updated locales [skip ci] 2020-11-05 16:25:18 -08:00
0e73d55c06 Updated locales [skip ci] 2020-11-04 16:25:09 -08:00
28a1eb7ee9 Updated locales [skip ci] 2020-11-03 16:25:17 -08:00
5ccee02391 Updated locales [skip ci] 2020-11-02 16:25:06 -08:00
cf14992283 Updated locales [skip ci] 2020-11-01 16:24:52 -08:00
8e29595b08 Updated locales [skip ci] 2020-10-31 17:23:24 -07:00
b3158c8813 Updated locales [skip ci] 2020-10-30 17:23:19 -07:00
84698c29bd Updated locales [skip ci] 2020-10-29 17:24:35 -07:00
153b001930 Updated locales [skip ci] 2020-10-28 17:23:20 -07:00
6e1b6232a4 Updated locales [skip ci] 2020-10-27 17:23:08 -07:00
41f18eb8dd Updated locales [skip ci] 2020-10-26 17:24:08 -07:00
6f820779f5 Updated locales [skip ci] 2020-10-25 17:24:07 -07:00
68c1cd7718 Updated locales [skip ci] 2020-10-24 16:24:17 -07:00
39059725f6 Updated locales [skip ci] 2020-10-23 16:23:04 -07:00
4c10890554 Updated locales [skip ci] 2020-10-22 16:24:29 -07:00
877d588f8f Updated locales [skip ci] 2020-10-21 16:23:38 -07:00
1698cc59c3 Updated locales [skip ci] 2020-10-20 16:24:37 -07:00
351783aa53 Updated locales [skip ci] 2020-10-19 16:25:11 -07:00
932f945b1f Updated locales [skip ci] 2020-10-18 16:24:28 -07:00
033b7f2c6d Updated locales [skip ci] 2020-10-17 16:24:43 -07:00
01d85cdbf6 Updated locales [skip ci] 2020-10-16 16:24:24 -07:00
87065716b0 Updated locales [skip ci] 2020-10-15 16:24:24 -07:00
c1f5244b7d Updated locales [skip ci] 2020-10-14 16:24:44 -07:00
e89d02eeeb Updated locales [skip ci] 2020-10-13 16:24:45 -07:00
66c6e62293 Updated locales [skip ci] 2020-10-12 16:25:27 -07:00
92239613b2 Updated locales [skip ci] 2020-10-11 16:24:32 -07:00
2adbd2f2d7 Updated locales [skip ci] 2020-10-10 16:24:44 -07:00
07158f5ffd Updated locales [skip ci] 2020-10-09 16:24:47 -07:00
158391330c Updated locales [skip ci] 2020-10-08 16:24:40 -07:00
3874b34a77 Updated locales [skip ci] 2020-10-07 16:24:45 -07:00
83abe3376b Updated locales [skip ci] 2020-10-06 16:24:49 -07:00
e85acd6c20 Updated locales [skip ci] 2020-10-05 16:25:08 -07:00
2410af484c Updated locales [skip ci] 2020-10-04 16:24:45 -07:00
5b9a28344e Updated locales [skip ci] 2020-10-03 16:25:03 -07:00
0c0c89debe Updated locales [skip ci] 2020-10-02 16:25:04 -07:00
5f8d3e51de Updated locales [skip ci] 2020-10-02 08:16:02 -07:00
87e3169a19 Translated using Weblate (Spanish)
Currently translated at 100.0% (100 of 100 strings)

Translation: MusicDL/interface
Translate-URL: http://translations.manuelcortez.net/projects/musicdl/interface/es/
2020-10-02 10:10:30 -05:00
3d7432f7b7 Updated locales [skip ci] 2020-10-01 16:24:26 -07:00
b0c0cdbdba Update translation catalogs [skip CI] 2020-10-01 10:03:41 -05:00
4cb07bd2f0 Changed frequency of building stuff [Skip CI] 2020-10-01 09:21:11 -05:00
ce2435dfb4 Updated locales [skip ci] 2020-10-01 07:14:58 -07:00
5c0d98fc9f fixed a path when generating translation files 2020-10-01 09:09:14 -05:00
893b075f06 Fix some gitlab changes 2020-09-30 17:56:16 -05:00
d7759c75a0 Removed coverage from hard requirements list 2020-09-30 17:48:53 -05:00
18f1a15a81 Started to work in automatic translations workflow 2020-09-30 17:48:24 -05:00
ad2189039e fixed python checking code no longer valid 2020-07-31 10:52:53 -05:00
57a23868e9 Updated russian translation 2020-07-30 05:18:21 -05:00
2a834559ec Fixed a small typo 2020-07-27 11:45:29 -05:00
9481d2868f Updated scripts to be executed on CI environment 2020-07-27 09:44:04 -05:00
fd791fe181 Modified update paths and files in order to deploy the next date based release 2020-07-27 09:39:54 -05:00
1477522681 removed i18n from the test suite [Skip CI] 2020-07-23 06:09:28 -05:00
81d0c48a8b Updated spanish translation [Skip CI] 2020-07-23 06:08:27 -05:00
8fe6302942 Added instructions to deal with translation catalogs 2020-07-23 06:07:54 -05:00
0f062a2fde Added some accelt controls 2020-07-20 03:38:58 -05:00
ca6e6b23bf Updated Readme [Skip CI] 2020-07-19 11:41:14 -05:00
ff8a7492e8 Reverted back to 32 bits support due to missing VLC stuff 2020-07-19 11:38:07 -05:00
aa2c46c618 Attempt to build a 64 bits version 2020-07-19 11:31:10 -05:00
d6e75f8604 Removed app description as it is displayed in windows notifications 2020-07-19 11:29:01 -05:00
abd83553b7 Safe filename function has been added to controller 2020-07-19 10:45:59 -05:00
4c2d696ee6 Always use safe filenames for downloads 2020-07-19 10:41:23 -05:00
884bcfadca Reverted back docker experiments 2020-07-19 10:31:34 -05:00
e342003de5 Fixed second typo 2020-07-19 08:53:29 -05:00
b9c763c4f4 New attempt 2020-07-19 08:36:30 -05:00
9ef726e762 Tune git install 2020-07-19 08:11:46 -05:00
d1ec62e41d Added CMd files 2020-07-19 08:05:00 -05:00
fa3ac19651 Added minGW32 path too 2020-07-19 07:59:20 -05:00
49d6d4093c Fix typos on path 2020-07-19 07:50:42 -05:00
a15f35822e silence progress in request url 2020-07-19 07:44:42 -05:00
3190e90974 Fixed a few tipos 2020-07-19 07:27:15 -05:00
7e2c6d6430 Added git path location correctly 2020-07-19 07:21:50 -05:00
c9b85f31f7 Docker experiments back 2020-07-19 07:10:55 -05:00
841df99d61 Write new alpha version info on every build 2020-07-18 01:52:41 -05:00
fdea954083 Added bootstrap updater into executable 2020-07-18 01:37:13 -05:00
444c132843 Revert back to gitlab CI file prior to the docker experiment 2020-07-18 01:23:44 -05:00
6e86359698 Set path 2020-07-18 01:02:03 -05:00
3f2fc9c9de Added poshgit 2020-07-18 00:52:46 -05:00
0d5cf787fc Refresh powershell env 2020-07-18 00:30:54 -05:00
f4872a1ecd Fixed syntax error 2020-07-18 00:18:45 -05:00
2d8b7b2ed6 Test 2020-07-18 00:17:00 -05:00
503dead2aa Added 32 bits only variant 2020-07-17 23:39:52 -05:00
19b7248074 Autoaccept choco install 2020-07-17 23:35:06 -05:00
b4c8a41757 Removed extra l 2020-07-17 23:32:41 -05:00
91acce2df9 Try to another docker image 2020-07-17 23:22:44 -05:00
3b7df1230c Making test 2020-07-17 23:04:32 -05:00
5e6749cf84 Change a syntax param 2020-07-17 22:52:33 -05:00
a94aaf4ce3 Fixed a command 2020-07-17 22:48:43 -05:00
c7ba0a86c1 Tried to install git again 2020-07-17 22:43:51 -05:00
7abdf00529 Invoque command used instead 2020-07-17 22:38:23 -05:00
b42c7d402f Remove the -command argument from invoque-expression 2020-07-17 22:32:20 -05:00
bff6b3d6c3 A few typos 2020-07-17 22:28:14 -05:00
3e284d5a8b use basic parsing 2020-07-17 22:21:39 -05:00
a690f116d2 Attempt to install git in windows docker 2020-07-17 22:18:40 -05:00
693a9f474e Merge branch 'master' of code.manuelcortez.net:manuelcortez/music-dl 2020-07-17 21:56:06 -05:00
5512b605e1 Started implementation of a failed docker based image 2020-07-17 21:55:44 -05:00
1051b81983 Added alpha version generation for Music DL and autoupdates 2020-07-17 17:51:02 -05:00
f0f23c9b05 Imported updater module directly from socializer 2020-07-17 17:50:18 -05:00
12a4ee65c3 Tidal: Added top:// command to retrieve the top rated tracks for a provided artist. [Skip CI] 2020-07-17 17:36:48 -05:00
96af01279a Updated changes 2020-07-17 16:37:33 -05:00
0cc6f97f01 Added metadata to m4a files downloaded from tidal 2020-07-13 16:04:20 -05:00
fef5c218b0 Fixed a couple of issues 2020-07-12 23:11:41 -05:00
c518d0ea08 Attempt to upload music DL to ftp 2020-07-12 21:57:53 -05:00
a2caaa6449 Attempt to use a virtual environment every time the CI is building 2020-07-12 20:52:01 -05:00
0a102fecc2 More requirements 2020-07-12 20:41:50 -05:00
f9f1a9c864 Added babel 2020-07-12 20:38:58 -05:00
10570c23b6 Added platform_utils as a dependency 2020-07-12 20:35:28 -05:00
42446107f7 Updated to python 3.8 2020-07-12 20:32:13 -05:00
7d9067a611 Fixed some issues in cx_freeze 2020-07-12 20:31:15 -05:00
b9302000f6 Updated changelog 2020-07-12 20:19:36 -05:00
cfd6a92c35 Added a few comments 2020-07-12 20:19:20 -05:00
f1f460bf0a small fix for VK2 2020-07-12 18:21:46 -05:00
561660b700 Added experimental support to VK 2020-07-10 17:38:30 -05:00
bcd3b7b36e Removed extra call to get_download_url 2020-07-08 17:16:35 -05:00
332365d53b Remove references to google discovery services 2020-07-08 16:39:44 -05:00
0768ecc24f Player receives metadata now 2020-07-08 16:39:17 -05:00
6aeffd57bb Added get_metadata into base songs 2020-07-08 13:14:50 -05:00
dae1df79ad Pass metadata to downloaders 2020-07-08 13:14:08 -05:00
c06df2092e Added tidal Settings and apply metadata functions 2020-07-08 13:12:33 -05:00
bb32d79dd7 Updated version numbers 2020-07-08 08:27:08 -05:00
27e422888b Removed pyinstaller spec file 2020-07-07 21:15:59 -05:00
b1f239fe82 Fixed tests 2020-07-07 21:14:22 -05:00
ff8c8ac22b Fixed zipfile generator 2020-07-07 17:56:44 -05:00
ea04cf2eb1 Fixed installer script 2020-07-07 17:52:15 -05:00
861fdb56b5 Started to adopt wxpython 4.1.0 2020-07-07 17:47:49 -05:00
e2d73ec446 Rewrote the CI config file to take advantage of the new runner 2020-07-07 17:33:09 -05:00
b4a0060756 Modified setup file 2020-07-07 17:21:31 -05:00
66f6f32916 Updated some tests. 2020-07-07 17:03:25 -05:00
01f639b809 fixed Youtube searches 2020-07-07 17:03:04 -05:00
10d60e3aa2 tidal: Add track number to results when tracks are part of an album in artist search 2019-10-06 11:26:09 -05:00
16d99aaaac Install tidalapi from their github repo 2019-10-06 11:05:11 -05:00
625931f40b Reverted commits to the last working state 2019-08-16 03:05:37 -05:00
6a188b7ec7 Revert "Added more settings in Tidal"
This reverts commit 207a7110ef111633ce9aae7829c19ad7556f62e3.

Revert to the last commit so the integration of music player features needs to be done in another way.
2019-08-16 02:59:04 -05:00
adc1317401 Added experimental changes for musicDL 2019-07-12 17:54:29 -05:00
207a7110ef Added more settings in Tidal 2019-07-08 16:43:31 -05:00
69452b69bd Updated changelog 2019-07-08 16:06:20 -05:00
084d3b8b10 Renamed extractors module to services 2019-07-08 13:04:00 -05:00
eedf897de0 Fixed a typo in player module 2019-07-08 12:34:42 -05:00
9ac26bc818 Imported paths module from Socializer. That means unicode paths should be fully supported 2019-07-08 12:29:41 -05:00
a65d6a82c0 Updated code for Version 0.6 2019-06-24 13:36:12 -05:00
659e436dc4 [YouTube]: disable transcode settings due to errors implementing this feature 2019-06-24 13:30:03 -05:00
ab0fc159f1 [tidal]: Fixed error that was resetting quality every time the config dialog was shown 2019-06-24 13:27:42 -05:00
18e90b7502 Updated translations 2019-06-24 13:20:53 -05:00
c8e83d2011 [zaycev.net]: Fixed a typo 2019-06-24 12:55:39 -05:00
4254f444db added a service category in settings for all services 2019-06-24 12:54:43 -05:00
7756d71b32 Added a progress bar for downloads in the application. Fixed a problem with format in Tidal 2019-06-24 12:45:09 -05:00
bb97d017b5 Updated changelog 2019-06-24 09:57:01 -05:00
64d076ce44 The search should be performed without blocking the GUI at all 2019-06-24 09:51:11 -05:00
d33205e84e Reload all extractors' config after saving changes in the settings dialog 2019-06-24 09:41:22 -05:00
bb411e7bbc [zaycev.net]: Added settings GUI 2019-06-21 17:12:15 -05:00
a80bfd53c1 Allow duplicate values in config files 2019-06-21 17:11:26 -05:00
75131a6fe6 Added settings to the zaycev service 2019-06-21 17:10:32 -05:00
d480e06ee3 Added settings section for tidal 2019-06-21 14:47:24 -05:00
1fcdd51358 Added setting to control the output device in libVLC 2019-06-21 13:12:54 -05:00
b254a4eb1b Download files using the new parameters from the base Extractor 2019-06-20 17:45:14 -05:00
93b066804b Defined a base extractor interface from where others will be derived. Added methods to retrieve if the transcoder should be enabled or not, and to retrieve the default file format, which also will be used as file extension in the saveDialog suggestions 2019-06-20 17:44:39 -05:00
edc46ee824 Added _format and bitrate parameters to the transcoder so it will be more flexible. The default for mp3 now is 320 KBPS 2019-06-20 17:24:56 -05:00
7786a31c2c Revert to LibVLc v2.x due to problems to pass arguments to 3.0.7 2019-06-20 17:11:53 -05:00
ffa02088ad Allow displaying up to 50 results in youtube 2019-06-20 10:19:18 -05:00
22b3b31895 Check if credentials exists for displaying this module in the services list 2019-06-20 10:18:49 -05:00
2e67a1ae63 Check if components should be displayer for a service 2019-06-18 16:42:31 -05:00
52265c4f3e Services may be disabled from config 2019-06-18 16:36:45 -05:00
b105dd649d Added WX locale 2019-06-18 16:17:07 -05:00
ad5569f26f Updated tests with new settings 2019-06-18 16:16:46 -05:00
b090d7f896 Add options for all extractors to be enabled or disabled 2019-06-18 16:16:29 -05:00
0447974029 Added base code for settings 2019-06-17 06:01:55 -05:00
26f2da1e6d Updated changelog 2019-06-13 17:43:20 -05:00
a8d6fa84b4 Updated libvlc.dll and libvlccore.dll to 3.0.7 2019-06-13 17:43:00 -05:00
93d21868e6 Added mutagen as a dependency for a future attempt to autotag downloaded files (especially useful for Tidal) 2019-06-13 17:42:21 -05:00
a04dd9c11b File extension when downloading will be determined by the extractor itself. Fixed an issue in youtube that was making VLC to receive encrypted streams only 2019-06-13 17:41:05 -05:00
daf1610054 Removed mail.ru extractor 2019-06-13 17:39:52 -05:00
76b06090e6 Updated VLC components to version 3.0.7 2019-06-13 17:33:28 -05:00
f080977e23 Define info as a conditional value in the song model 2019-06-13 12:34:07 -05:00
cb5b0707bb Add track info from tidal API into song's classes 2019-06-13 12:33:00 -05:00
4e5941cdf4 Fixed issue with logging unicode characters in some cp1252 systems 2019-06-12 22:43:57 -05:00
6a16a66b5e fixed issue with search by title 2019-06-12 22:43:20 -05:00
dd23ce6adf fixed conditional import with testing suite 2019-06-12 22:42:51 -05:00
99d02c97f0 Add available extractors dynamically from imports of extractors package 2019-06-12 22:28:58 -05:00
d0491d8dd0 Added tidal as a service 2019-06-12 17:44:45 -05:00
217 changed files with 2551 additions and 2398 deletions

View File

@ -1,106 +1,121 @@
# This CI configuration file is used to build all available versions of MusicDL. It's intended to launch a new version when a tag is pushed to master.
# In order to work, A Gitlab Runner with Windows Server 2016 Standard with the following packages is used:
# * Latest python for both 2.7 and 3.x branches.
# * Py2exe for Python 2.7
# * Microsoft Visual C++ 2015 redistributable files
# * Nsis
# Declare some variables dependent on the operating system where the runner is installed.
# This CI file assumes we install everything in C:\ (Python 2.7, 3.7 and Nsis).
variables:
PYTHON3: "C:\\python37\\python.exe"
PYINSTALLER: "C:\\python37\\scripts\\pyinstaller.exe"
NSIS: "C:\\nsis\\makensis.exe"
### Stage list
# Build: This will be the main stage generating stuff in the dist folder. Jobs present in this stage will run py2exe or pyinstaller files accordingly.
# pack: Jobs in this stage will take the dist folder and zip it or generate an exe file.
stages: stages:
- test - test
- generate_docs
- build - build
- pack - versions
- upload
# Python 3 tests program64:
test_py3: interruptible: true
stage: test
tags: tags:
- windows
- windows10 - windows10
before_script:
- '%PYTHON3% -v'
- '%PYTHON3% -m pip install --upgrade pip'
- '%PYTHON3% -m pip install --upgrade -r requirements.txt'
script:
- cd src
- '%PYTHON3% -m coverage run run_tests.py'
- '%PYTHON3% -m coverage report --omit="test*"'
coverage: '/TOTAL.+ ([0-9]{1,3}%)/'
only:
- master
- tags
- schedule_pipelines
# Python 3 version. During this job, the dist folder, containing all files to distribute, will be generated
# and passed to build_zip and build_setup jobs.
build_py3:
stage: build stage: build
tags: variables:
- windows10 PYTHON: "C:\\python310\\python.exe"
# Update stuff before building versions
before_script: before_script:
- '%PYTHON3% -v' - Set-Variable -Name "time" -Value (date -Format "%H:%m")
- '%PYTHON3% -m pip install --upgrade pip' - echo ${time}
- '%PYTHON3% -m pip install --upgrade -r requirements.txt' - echo "started by ${GITLAB_USER_NAME}"
- '&$env:PYTHON -V'
- '&$env:PYTHON -m pip install --upgrade pip'
- '&$env:PYTHON -m venv env'
- 'env\Scripts\Activate.ps1'
- 'python -m pip install --upgrade -r requirements.txt'
script: script:
- cd src - cd src
- '%PYINSTALLER% main.spec' - 'python write_version_data.py'
# Build this automatically only when tags are pushed to master or when a pipeline has been scheduled by Gitlab. - 'python setup.py build'
only: - cd ..
- tags - 'mkdir build'
- schedule_pipelines
# Make the dist folder available to other jobs.
# It will expire in 30 mins as we won't need the dist folder after the pipeline is completed.
artifacts:
paths:
- src\\dist
expire_in: 30 mins
# This job takes the src\\dist folder generated in build_py3 and creates a zip file, which will be uploaded to the repository's artifacts.
zip_py3:
stage: pack
tags:
- windows10
only:
- tags
- schedule_pipelines
dependencies:
- build_py3
script:
- cd scripts - cd scripts
- '%PYTHON3% prepare_zipversion.py' - 'python prepare_zipversion.py'
- cd .. - cd ..
- move src\music_dl.zip music_dl.zip - move src\music_dl.zip build\music_dl_x64.zip
# No expiry date as there will be only releases in the artifacts. - 'move src/dist build/program64'
- 'move src/installer.nsi build'
only:
- schedules
- master
artifacts: artifacts:
paths: paths:
- music_dl.zip - build
expire_in: 1 day
# This job takes the src\\dist generated in build_py3 and creates a setup installer file. program32:
build_setup: interruptible: true
stage: pack
tags: tags:
- windows
- windows10 - windows10
only: stage: build
- tags variables:
- schedule_pipelines PYTHON: "C:\\python310-32\\python.exe"
dependencies: before_script:
- build_py3 - Set-Variable -Name "time" -Value (date -Format "%H:%m")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
- '&$env:PYTHON -V'
- '&$env:PYTHON -m pip install --upgrade pip'
- '&$env:PYTHON -m venv env'
- 'env\Scripts\Activate.ps1'
- 'python -m pip install https://github.com/josephsl/wxpy32whl/raw/main/wxPython-4.2.0-cp310-cp310-win32.whl'
- 'python -m pip install --upgrade -r requirements.txt'
script: script:
- cd src - cd src
- '%NSIS% installer.nsi' - 'python write_version_data.py'
- 'python setup.py build'
- cd .. - cd ..
- move src\music_dl* . - 'mkdir build'
- cd scripts
- 'python prepare_zipversion.py'
- cd ..
- move src\music_dl.zip build\music_dl_x86.zip
- 'move src/dist build/program32'
only:
- schedules
- master
artifacts: artifacts:
paths: paths:
- "music_dl_*" - build
name: music_dl expire_in: 1 day
generate_versions:
stage: versions
tags:
- windows
- windows10
variables:
NSIS: "C:\\program files (x86)\\nsis\\makensis.exe"
before_script:
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
script:
- mkdir artifacts
- 'cd build'
- '&$env:NSIS installer.nsi'
- move *.exe ../artifacts
- move *.zip ../artifacts
only:
- tags
- master
- schedules
artifacts:
paths:
- artifacts
expire_in: 1 day
upload:
image:
name: amazon/aws-cli
entrypoint: [""]
tags:
- docker
only:
- tags
- master
interruptible: true
stage: upload
script:
- aws --version
- aws --endpoint-url https://s3.us-west-001.backblazeb2.com s3 cp artifacts s3://$S3_BUCKET/music_dl/ --recursive

View File

@ -12,26 +12,32 @@ MusicDL is an app for downloading music directly from services like Youtube, zay
See the requirements.txt, located in the root of this repository. Additionally, take into account the following. See the requirements.txt, located in the root of this repository. Additionally, take into account the following.
* In case you want to create your own distributable version with Python 2, you'll need py2exe.
## running ## running
Run the file main.py, located in the src directory. Run the file main.py, located in the src directory.
## Building ## Building
### Python 3 I have provided a setup.py file for cx_freeze, so you should be able to do something like:
I have provided a main.spec file for pyinstaller, so you should be able to do something like: > python setup.py build
> C:\python3\scripts\pyinstaller.exe main.spec
And start building. Check the dist folder for results. And start building. Check the dist folder for results.
### Python 2 ## Updating translation catalog
If you are using Python 2.x and want to build MusicDL, there is a setup.py file made for pyinstaller aswell. Just run it the usual way: Every time there are new strings in the application a translations catalog update must be performed with the following commands in the src directory:
> C:\python2\python.exe setup.py py2exe > python setup.py extract_messages -o musicdl.pot --msgid-bugs-address "manuel@manuelcortez.net" --copyright-holder "Manuel Cortez" --input-dirs .
> python setup.py update_catalog --input-file musicdl.pot --domain musicdl --output-dir locales --ignore-obsolete true
And You will get a distributable version of MusicDL. And after updating translations they should be compiled with:
> python setup.py compile_catalog --statistics -d locales --domain musicdl
## Adding new translations
The procedure for adding new translations is also easy, thanks to the following command. Just replace xx for the new locale name to add:
> python setup.py extract_messages -o musicdl.pot --msgid-bugs-address "manuel@manuelcortez.net" --copyright-holder "Copyright (C) 2019, 2020 Manuel Cortez" --input-dirs .
> python setup.py init_catalog --domain musicdl --input-file musicdl.pot -d locales --locale xx

View File

@ -1,5 +1,49 @@
## Changelog ## Changelog
## Changes on January
* Application improvements:
* Updated VLC components to V3.0.11.
* Updated SSL/TLS plugin in vlc which should make youtube playback less buggy.
* MusicDL now builds with Python V 3.9.1.
* zaycev.net:
* Disabled service by default as it will not work outside Russia.
## Version 0.7
* Application improvements:
* MusicDL no longer adds invalid characters when attempting to download a file.
* Tidal:
* Added a new search mode for the service to retrieve the top rated tracks of a provided artist. The sintax is top://artist.
* In the settings dialog, you can control wether Albums, compilations and singles will be added when searching by artist (by using artist://...).
* When searching by artists, results that belong to an album will be numbered.
* Downloads will be tagged with title, album, artist and track number provided by tidal.
* It is possible to download an original version in high and low quality. Before, those versions were encoded to mp3 from an m4a file. Now the M4a file can be retrieved by using the checkbox in the tidal settings page.
* YouTube:
* Fixed search algorithm for Youtube videos.
* Updated Youtube-Dl to version 2020.6.16.1
* re-added VK module. By default, this module searches up to 50 results but you can increase it up to 200 if needed from the services settings.
## Version 0.6
* Added a settings dialog for the application, from this dialog you will be able to find some general settings, available for MusicDL, and service's settings. Every service defines certain specific settings.
* When searching in any service, the search should be performed without freezing the application window.
* When transcoding to mp3, the default bitrate now will be 320 KBPS instead of 192.
* When downloading, besides the status bar, there is a progress bar which will be updated with the results for the current download.
* From the settings dialog, it is possible to switch between all available output devices in the machine, so MusicDL can output audio to a different device than the default in windows.
* Added a new and experimental extractor for supporting tidal.
* Take into account that this extractor requires you to have a paid account on tidal. Depending in the account level, you will be able to play and download music in high quality or lossless audio. MusicDL will handle both. Lossless audio will be downloaded as flac files, and high quality audio will be downloaded as transcoded 320 KBPS mp3.
* There is a new search mode supported in this service. You can retrieve all work for a certain artist by using the protocol artist://, plus the name of the artist you want to retrieve. For example, artist://The beatles will retrieve everything made by the beatles available in the service. The search results will be grouped by albums, compilations and singles, in this order. Depending in the amount of results to display, this may take a long time.
* Due to recent problems with mail.ru and unavailable content in most cases, the service has been removed from MusicDL.
* YouTube:
* Fixed a long standing issue with playback of some elements, due to Youtube sending encrypted versions of these videos. Now playback should be better.
* Updated YoutubeDL to version 2019.6.7
* Now it is possible to load 50 items for searches as opposed to the previous 20 items limit. This setting can be controlled in the service's preferences
* zaycev.net:
* Fixed extractor for searching and playing music in zaycev.net.
* Unfortunately, it seems this service works only in the russian Federation and some other CIS countries due to copyright reasons.
* Updated Spanish translations.
## Version 0.4 ## Version 0.4
* Fixed an error when creating a directory located in %appdata%, when using MusicDL as an installed version. MusicDL should be able to work normally again. * Fixed an error when creating a directory located in %appdata%, when using MusicDL as an installed version. MusicDL should be able to work normally again.
@ -7,7 +51,7 @@
* MusicDL will no longer set volume at 50% when it starts. It will save the volume in a settings file, so it will remember volume settings across restarts. * MusicDL will no longer set volume at 50% when it starts. It will save the volume in a settings file, so it will remember volume settings across restarts.
* Added an option in the help menu to report an issue. You can use this feature for sending reports of problems you have encountered while using the application. You will need to provide your email address, though it will not be public anywhere. Your email address will be used only for contacting you if necessary. * Added an option in the help menu to report an issue. You can use this feature for sending reports of problems you have encountered while using the application. You will need to provide your email address, though it will not be public anywhere. Your email address will be used only for contacting you if necessary.
* changes in Youtube module: * changes in Youtube module:
* Updated YoutubeDL to version 2018.10.05 * Updated YoutubeDL to latest version.
## Version 0.3 ## Version 0.3

View File

@ -1,12 +1,13 @@
coverage wxpython
wxpython==4.0.3
requests requests
bs4 bs4
pypubsub pypubsub
python-vlc yt-dlp
google-api-python-client python-mpv
youtube-dl cx_freeze
pyinstaller
isodate
configobj configobj
winpaths winpaths
mutagen
babel
tidalapi
git+https://github.com/accessibleapps/platform_utils

View File

@ -0,0 +1,21 @@
#! /usr/bin/env python
import os
import json
import datetime
print("Generating update files for Socializer...")# Determine if we are going to write stable or alpha update file.
version = datetime.datetime.now().strftime("%Y.%m.%d")
version_type = "latest"
print("Version detected: %s" % (version_type,))
# Read update description and URL'S
description = os.environ.get("CI_COMMIT_MESSAGE")
urls = dict(Windows32="https://files.mcvsoftware.com/music_dl/latest/music_dl_x86.zip", Windows64="https://files.mcvsoftware.com/music_dl/latest/music_dl_x64.zip")
# build the main dict object
data = dict(current_version=version, description=description, downloads=urls)
print("Generating file with the following arguments: %r" % (data,))
updatefile = "latest.json"
f = open(updatefile, "w")
json.dump(data, f, ensure_ascii=False)
f.close()

View File

@ -1,242 +1,19 @@
# SOME DESCRIPTIVE TITLE. # Translations template for musicDL.
# Copyright (C) YEAR ORGANIZATION # Copyright (C) 2020 Manuel Cortez
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # This file is distributed under the same license as the musicDL project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
# #
#, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: musicDL 2020.7.23\n"
"POT-Creation-Date: 2018-03-03 09:38+Hora estándar central (México)\n" "Report-Msgid-Bugs-To: manuel@manuelcortez.net\n"
"POT-Creation-Date: 2020-12-29 05:08-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: ENCODING\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: Babel 2.9.0\n"
#: ../src\application.py:7
msgid " Is an application that will allow you to download music from popular sites such as youtube, zaycev.net."
msgstr ""
#: ../src\application.py:12
msgid "Manuel Cortez (Spanish)"
msgstr ""
#: ../src\controller\mainController.py:27
msgid "Ready"
msgstr ""
#: ../src\controller\mainController.py:42
msgid "Showing {0} results."
msgstr ""
#: ../src\controller\mainController.py:46
msgid "Shuffle on"
msgstr ""
#: ../src\controller\mainController.py:118
#: ../src\controller\mainController.py:138 ../src\wxUI\mainWindow.py:13
#: ../src\wxUI\mainWindow.py:62
msgid "Play"
msgstr ""
#: ../src\controller\mainController.py:121
#: ../src\controller\mainController.py:133
msgid "Pause"
msgstr ""
#: ../src\controller\mainController.py:213
msgid "File downloaded: {0}"
msgstr ""
#: ../src\controller\mainController.py:236
msgid "Searching {0}... "
msgstr ""
#: ../src\controller\mainController.py:242
msgid "No results found. "
msgstr ""
#: ../src\controller\player.py:43
msgid "Error playing {0}. {1}."
msgstr ""
#: ../src\controller\player.py:49
msgid "Playing {0}."
msgstr ""
#: ../src\controller\player.py:117 ../src\utils.py:53
msgid "Downloading {0}."
msgstr ""
#: ../src\controller\player.py:122 ../src\utils.py:63
msgid "Downloading {0} ({1}%)."
msgstr ""
#: ../src\controller\player.py:133
msgid "Error"
msgstr ""
#: ../src\controller\player.py:133
msgid "There was an error while trying to access the file you have requested."
msgstr ""
#: ../src\update\utils.py:27
msgid "%d day, "
msgstr ""
#: ../src\update\utils.py:29
msgid "%d days, "
msgstr ""
#: ../src\update\utils.py:31
msgid "%d hour, "
msgstr ""
#: ../src\update\utils.py:33
msgid "%d hours, "
msgstr ""
#: ../src\update\utils.py:35
msgid "%d minute, "
msgstr ""
#: ../src\update\utils.py:37
msgid "%d minutes, "
msgstr ""
#: ../src\update\utils.py:39
msgid "%s second"
msgstr ""
#: ../src\update\utils.py:41
msgid "%s seconds"
msgstr ""
#: ../src\update\wxUpdater.py:9
msgid "New version for %s"
msgstr ""
#: ../src\update\wxUpdater.py:9
msgid ""
"There's a new %s version available. Would you like to download it now?\n"
"\n"
" %s version: %s\n"
"\n"
"Changes:\n"
"%s"
msgstr ""
#: ../src\update\wxUpdater.py:16
msgid "Download in Progress"
msgstr ""
#: ../src\update\wxUpdater.py:16
msgid "Downloading the new version..."
msgstr ""
#: ../src\update\wxUpdater.py:26
msgid "Updating... %s of %s"
msgstr ""
#: ../src\update\wxUpdater.py:29
msgid "Done!"
msgstr ""
#: ../src\update\wxUpdater.py:29
msgid "The update has been downloaded and installed successfully. Press OK to continue."
msgstr ""
#: ../src\wxUI\mainWindow.py:14 ../src\wxUI\mainWindow.py:63
msgid "Stop"
msgstr ""
#: ../src\wxUI\mainWindow.py:15 ../src\wxUI\mainWindow.py:61
msgid "Previous"
msgstr ""
#: ../src\wxUI\mainWindow.py:16 ../src\wxUI\mainWindow.py:64
msgid "Next"
msgstr ""
#: ../src\wxUI\mainWindow.py:17
msgid "Shuffle"
msgstr ""
#: ../src\wxUI\mainWindow.py:18
msgid "Volume down"
msgstr ""
#: ../src\wxUI\mainWindow.py:19
msgid "Volume up"
msgstr ""
#: ../src\wxUI\mainWindow.py:20
msgid "Mute"
msgstr ""
#: ../src\wxUI\mainWindow.py:22
msgid "About {0}"
msgstr ""
#: ../src\wxUI\mainWindow.py:23
msgid "Check for updates"
msgstr ""
#: ../src\wxUI\mainWindow.py:24
msgid "What's new in this version?"
msgstr ""
#: ../src\wxUI\mainWindow.py:25
msgid "Visit website"
msgstr ""
#: ../src\wxUI\mainWindow.py:26
msgid "Player"
msgstr ""
#: ../src\wxUI\mainWindow.py:27
msgid "Help"
msgstr ""
#: ../src\wxUI\mainWindow.py:37
msgid "search"
msgstr ""
#: ../src\wxUI\mainWindow.py:42
msgid "Search in"
msgstr ""
#: ../src\wxUI\mainWindow.py:45
msgid "Search"
msgstr ""
#: ../src\wxUI\mainWindow.py:49
msgid "Results"
msgstr ""
#: ../src\wxUI\mainWindow.py:55
msgid "Position"
msgstr ""
#: ../src\wxUI\mainWindow.py:58
msgid "Volume"
msgstr ""
#: ../src\wxUI\mainWindow.py:100
msgid "Audio Files(*.mp3)|*.mp3"
msgstr ""
#: ../src\wxUI\mainWindow.py:100
msgid "Save this file"
msgstr ""
#: ../src\wxUI\menus.py:7
msgid "Play/Pause"
msgstr ""
#: ../src\wxUI\menus.py:9
msgid "Download"
msgstr ""

View File

@ -6,9 +6,6 @@ import sys
def create_archive(): def create_archive():
os.chdir("..\\src") os.chdir("..\\src")
print("Creating zip archive...") print("Creating zip archive...")
if sys.version[0] == "3":
folder = "dist/main"
else:
folder = "dist" folder = "dist"
shutil.make_archive("music_dl", "zip", folder) shutil.make_archive("music_dl", "zip", folder)
# if os.path.exists("dist"): # if os.path.exists("dist"):

80
scripts/upload.py Normal file
View File

@ -0,0 +1,80 @@
#! /usr/bin/env python
"""
Important note: for this script to work, the following conditions should be met:
* There must be ftp server data, via environment variables (FTP_SERVER, FTP_USERNAME and FTP_PASSWORD) or via arguments to the script call (in the prior order). Connection to this server is done via default ftp port via TLS.
* this code assumes it's going to connect to an FTP via TLS.
* If the version to upload is alpha, there's not need of an extra variable. Otherwise, CI_COMMIT_TAG should point to a version as vx.x, where v is a literal and x are numbers, example may be v0.18, v0.25, v0.3. This variable should be set in the environment.
* Inside the ftp server, the following directory structure will be expected: manuelcortez.net/static/files/music_dl. The script will create the <version> folder or alpha if needed.
* The script will upload all .exe, .zip and .json files located in the root directory from where it was called. The json files are uploaded to manuelcortez.net/static/files/music_dl/update and other files are going to manuelcortez.net/static/files/music_dl/<version>.
"""
import sys
import os
import glob
import ftplib
transferred=0
class MyFTP_TLS(ftplib.FTP_TLS):
"""Explicit FTPS, with shared TLS session"""
def ntransfercmd(self, cmd, rest=None):
conn, size = ftplib.FTP.ntransfercmd(self, cmd, rest)
if self._prot_p:
conn = self.context.wrap_socket(conn,
server_hostname=self.host,
session=self.sock.session) # this is the fix
return conn, size
def convert_bytes(n):
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
if n >= P:
return '%.2fPb' % (float(n) / T)
elif n >= T:
return '%.2fTb' % (float(n) / T)
elif n >= G:
return '%.2fGb' % (float(n) / G)
elif n >= M:
return '%.2fMb' % (float(n) / M)
elif n >= K:
return '%.2fKb' % (float(n) / K)
else:
return '%d' % n
def callback(progress):
global transferred
transferred = transferred+len(progress)
print("Uploaded {}".format(convert_bytes(transferred),))
ftp_server = os.environ.get("FTP_SERVER") or sys.argv[1]
ftp_username = os.environ.get("FTP_USERNAME") or sys.argv[2]
ftp_password = os.environ.get("FTP_PASSWORD") or sys.argv[3]
version = os.environ.get("CI_COMMIT_TAG") or "latest"
version = version.replace("v", "")
print("Uploading files to the Socializer server...")
connection = ftplib.FTP(ftp_server)
print("Connected to FTP server {}".format(ftp_server,))
connection.login(user=ftp_username, passwd=ftp_password)
#connection.prot_p()
print("Logged in successfully")
connection.cwd("music_dl")
if version not in connection.nlst():
print("Creating version directory {} because does not exists...".format(version,))
connection.mkd(version)
if "update" not in connection.nlst():
print("Creating update info directory because does not exists...")
connection.mkd("update")
connection.cwd(version)
print("Moved into version directory")
files = glob.glob("*.zip")+glob.glob("*.exe")+glob.glob("*.json")
print("These files will be uploaded into the version folder: {}".format(files,))
for file in files:
transferred = 0
print("Uploading {}".format(file,))
with open(file, "rb") as f:
if file.endswith("json"):
connection.storbinary('STOR ../update/%s' % file, f, callback=callback, blocksize=1024*1024)
else:
connection.storbinary('STOR %s' % file, f, callback=callback, blocksize=1024*1024)
print("Upload completed. exiting...")
connection.quit()

View File

@ -1,2 +1,29 @@
[main] [main]
volume = integer(default=50) volume = integer(default=50)
language = string(default="system")
output_device = string(default="Default")
[services]
[[tidal]]
enabled = boolean(default=True)
session_id=string(default="")
token_type=string(default="")
access_token=string(default="")
refresh_token=string(default="")
quality=string(default="high")
avoid_transcoding = boolean(default=False)
include_albums = boolean(default=True)
include_compilations = boolean(default=True)
include_singles = boolean(default=True)
[[vk]]
enabled = boolean(default=True)
max_results = integer(default=20)
[[youtube]]
enabled = boolean(default=True)
max_results = integer(default=20)
transcode = boolean(default=True)
[[zaycev]]
enabled = boolean(default=False)

View File

@ -1,17 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys
python_version = int(sys.version[0])
name = "MusicDL" name = "MusicDL"
version = "0.5" author = "MCV Software"
author = "Manuel Cortéz" authorEmail = "info@mcvsoftware.com"
authorEmail = "manuel@manuelcortez.net" copyright = "Copyright (C) 2019-2022, MCV Software"
copyright = "Copyright (C) 2019, Manuel Cortez"
description = name+_(u" Is an application that will allow you to download music from popular sites such as youtube, zaycev.net.") description = name+_(u" Is an application that will allow you to download music from popular sites such as youtube, zaycev.net.")
url = "https://manuelcortez.net/music_dl" url = "https://mcvsoftware.com/music_dl"
update_url = "https://manuelcortez.net/music_dl/update"
# The short name will be used for detecting translation files. See languageHandler for more details. # The short name will be used for detecting translation files. See languageHandler for more details.
short_name = "musicdl" short_name = "musicdl"
translators = [_(u"Manuel Cortez (Spanish)"), _("Valeria K (Russian)"), ] translators = [_(u"Manuel Cortez (Spanish)")]
bts_name = "music_dl" bts_name = "music_dl"
bts_access_token = "fe3j2ijirvevv9" bts_access_token = "fe3j2ijirvevv9"
bts_url = "https://issues.manuelcortez.net" bts_url = "https://issues.manuelcortez.net"
update_url = "https://files.mcvsoftware.com/music_dl/update/latest.json"
version = "2020.07.23"

View File

@ -15,4 +15,3 @@ def setup ():
global app global app
log.debug("Loading global app settings...") log.debug("Loading global app settings...")
app = config_utils.load_config(os.path.join(storage.data_directory, MAINFILE), os.path.join(paths.app_path(), MAINSPEC)) app = config_utils.load_config(os.path.join(storage.data_directory, MAINFILE), os.path.join(paths.app_path(), MAINSPEC))

View File

@ -6,8 +6,8 @@ import string
class ConfigLoadError(Exception): pass class ConfigLoadError(Exception): pass
def load_config(config_path, configspec_path=None, *args, **kwargs): def load_config(config_path, configspec_path=None, *args, **kwargs):
if os.path.exists(config_path): # if os.path.exists(config_path):
clean_config(config_path) # clean_config(config_path)
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True) spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
try: try:
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs) config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
import config
from utils import get_services
from wxUI.configuration import configurationDialog
from . import player
class configuration(object):
def __init__(self):
self.view = configurationDialog(_("Settings"))
self.create_config()
self.view.get_response()
self.save()
def create_config(self):
self.output_devices = player.player.get_output_devices()
self.view.create_general(output_devices=[i for i in self.output_devices])
current_output_device = config.app["main"]["output_device"]
for i in self.output_devices:
# here we must compare against the str version of the vlc's device identifier.
if i == current_output_device:
self.view.set_value("general", "output_device", i)
break
self.view.realize()
extractors = get_services(import_all=True)
for i in extractors:
if hasattr(i, "settings"):
panel = getattr(i, "settings")(self.view.notebook)
self.view.notebook.InsertSubPage(1, panel, panel.name)
panel.load()
if hasattr(panel, "on_enabled"):
panel.on_enabled()
def save(self):
selected_output_device = self.view.get_value("general", "output_device")
if config.app["main"]["output_device"] != selected_output_device:
config.app["main"]["output_device"] = selected_output_device
player.player.set_output_device(config.app["main"]["output_device"])
for i in range(0, self.view.notebook.GetPageCount()):
page = self.view.notebook.GetPage(i)
if hasattr(page, "save"):
page.save()
config.app.write()

View File

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" main controller for MusicDL""" """ main controller for MusicDL"""
from __future__ import unicode_literals # at top of module
import types import types
import webbrowser import webbrowser
import wx import wx
@ -13,17 +12,11 @@ from pubsub import pub
from issueReporter import issueReporter from issueReporter import issueReporter
from wxUI import mainWindow, menus from wxUI import mainWindow, menus
from update import updater from update import updater
from . import player from utils import get_services
from . import player, configuration
log = logging.getLogger("controller.main") log = logging.getLogger("controller.main")
def get_extractors():
""" Function for importing everything wich is located in the extractors package and has a class named interface."""
import extractors
module_type = types.ModuleType
classes = [m.interface for m in extractors.__dict__.values() if type(m) == module_type and hasattr(m, 'interface')]
return sorted(classes, key=lambda c: c.name)
class Controller(object): class Controller(object):
def __init__(self): def __init__(self):
@ -32,7 +25,7 @@ class Controller(object):
# Setting up the player object # Setting up the player object
player.setup() player.setup()
# Get main window # Get main window
self.window = mainWindow.mainWindow() self.window = mainWindow.mainWindow(extractors=[i.interface.name for i in get_services()])
log.debug("Main window created") log.debug("Main window created")
self.window.change_status(_(u"Ready")) self.window.change_status(_(u"Ready"))
# Here we will save results for searches as song objects. # Here we will save results for searches as song objects.
@ -67,6 +60,7 @@ class Controller(object):
widgetUtils.connect_event(self.window.list, widgetUtils.LISTBOX_ITEM_ACTIVATED, self.on_activated) widgetUtils.connect_event(self.window.list, widgetUtils.LISTBOX_ITEM_ACTIVATED, self.on_activated)
widgetUtils.connect_event(self.window.list, widgetUtils.KEYPRESS, self.on_keypress) widgetUtils.connect_event(self.window.list, widgetUtils.KEYPRESS, self.on_keypress)
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_play, menuitem=self.window.player_play) widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_play, menuitem=self.window.player_play)
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_settings, menuitem=self.window.settings)
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_next, menuitem=self.window.player_next) widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_next, menuitem=self.window.player_next)
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_previous, menuitem=self.window.player_previous) widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_previous, menuitem=self.window.player_previous)
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_stop, menuitem=self.window.player_stop) widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_stop, menuitem=self.window.player_stop)
@ -93,10 +87,24 @@ class Controller(object):
pub.subscribe(self.change_status, "change_status") pub.subscribe(self.change_status, "change_status")
pub.subscribe(self.on_download_finished, "download_finished") pub.subscribe(self.on_download_finished, "download_finished")
pub.subscribe(self.on_notify, "notify") pub.subscribe(self.on_notify, "notify")
pub.subscribe(self.on_update_progress, "update-progress")
# Event functions. These functions will call other functions in a thread and are bound to widget events. # Event functions. These functions will call other functions in a thread and are bound to widget events.
def on_update_progress(self, value):
wx.CallAfter(self.window.progressbar.SetValue, value)
def on_settings(self, *args, **kwargs):
settings = configuration.configuration()
self.reload_extractors()
def on_search(self, *args, **kwargs): def on_search(self, *args, **kwargs):
utils.call_threaded(self.search) text = self.window.get_text()
if text == "":
return
extractor = self.window.extractor.GetValue()
self.change_status(_(u"Searching {0}... ").format(text))
utils.call_threaded(self.search, text=text, extractor=extractor)
def on_activated(self, *args, **kwargs): def on_activated(self, *args, **kwargs):
self.on_play() self.on_play()
@ -107,15 +115,15 @@ class Controller(object):
elif ev.GetKeyCode() == wx.WXK_SPACE: elif ev.GetKeyCode() == wx.WXK_SPACE:
return self.on_play_pause() return self.on_play_pause()
elif ev.GetKeyCode() == wx.WXK_LEFT and ev.ShiftDown(): elif ev.GetKeyCode() == wx.WXK_LEFT and ev.ShiftDown():
position = player.player.player.get_time() position = player.player.player.get_position()
if position > 5000: if position > 5000:
player.player.player.set_time(position-5000) player.player.player.set_position(position-5000)
else: else:
player.player.player.set_time(0) player.player.player.set_position(0)
return return
elif ev.GetKeyCode() == wx.WXK_RIGHT and ev.ShiftDown(): elif ev.GetKeyCode() == wx.WXK_RIGHT and ev.ShiftDown():
position = player.player.player.get_time() position = player.player.player.get_position()
player.player.player.set_time(position+5000) player.player.player.set_position(position+5000)
return return
elif ev.GetKeyCode() == wx.WXK_UP and ev.ControlDown(): elif ev.GetKeyCode() == wx.WXK_UP and ev.ControlDown():
return self.on_volume_up() return self.on_volume_up()
@ -128,12 +136,12 @@ class Controller(object):
ev.Skip() ev.Skip()
def on_play_pause(self, *args, **kwargs): def on_play_pause(self, *args, **kwargs):
if player.player.player.is_playing() == 1: if player.player.player.playback_time != None and player.player.player.pause == False:
self.window.play.SetLabel(_(u"Play")) self.window.play.SetLabel(_("Play"))
return player.player.pause() return player.player.pause()
else: else:
self.window.play.SetLabel(_(u"Pause")) self.window.play.SetLabel(_("Pause"))
return player.player.player.play() player.player.player.pause = False
def on_next(self, *args, **kwargs): def on_next(self, *args, **kwargs):
return utils.call_threaded(player.player.next) return utils.call_threaded(player.player.next)
@ -178,18 +186,18 @@ class Controller(object):
def on_download(self, *args, **kwargs): def on_download(self, *args, **kwargs):
item = self.results[self.window.get_item()] item = self.results[self.window.get_item()]
log.debug("Starting requested download: {0} (using extractor: {1})".format(item.title, self.extractor.name))
f = "{0}.mp3".format(item.format_track())
if item.download_url == "": if item.download_url == "":
item.get_download_url() item.get_download_url()
path = self.window.get_destination_path(f) log.debug("Starting requested download: {0} (using extractor: {1})".format(item.title, self.extractor.name))
f = "{item_name}.{item_extension}".format(item_name=item.format_track(), item_extension=item.extractor.get_file_format())
path = self.window.get_destination_path(utils.safe_filename(f))
if path != None: if path != None:
log.debug("User has requested the following path: {0}".format(path,)) log.debug("User has requested the following path: {0}".format(path,))
if self.extractor.needs_transcode == True: # Send download to vlc based transcoder if self.extractor.transcoder_enabled() == True: # Send download to vlc based transcoder
utils.call_threaded(player.player.transcode_audio, item, path) utils.call_threaded(player.player.transcode_audio, item, path, _format=item.extractor.get_file_format(), metadata=item.get_metadata())
else: else:
log.debug("downloading %s URL to %s filename" % (item.download_url, path,)) log.debug("downloading %s URL to %s filename" % (item.download_url, path,))
utils.call_threaded(utils.download_file, item.download_url, path) utils.call_threaded(utils.download_file, item.download_url, path, metadata=item.get_metadata())
def on_set_volume(self, *args, **kwargs): def on_set_volume(self, *args, **kwargs):
volume = self.window.vol_slider.GetValue() volume = self.window.vol_slider.GetValue()
@ -197,13 +205,16 @@ class Controller(object):
def on_time_change(self, event, *args, **kwargs): def on_time_change(self, event, *args, **kwargs):
p = event.GetPosition() p = event.GetPosition()
player.player.player.set_position(p/100.0) if player.player.player != None:
progress = int((player.player.player.get_length()/100)*p)
player.player.player.set_position(progress)
event.Skip() event.Skip()
def on_timer(self, *args, **kwargs): def on_timer(self, *args, **kwargs):
if not self.window.time_slider.HasFocus(): if not self.window.time_slider.HasFocus():
progress = player.player.player.get_position()*100 if player.player.player.playback_time != None:
self.window.time_slider.SetValue(progress) progress = (player.player.player.playback_time/(player.player.player.playback_time+player.player.player.playtime_remaining))*100
self.window.time_slider.SetValue(int(progress))
def on_close(self, event): def on_close(self, event):
log.debug("Exiting...") log.debug("Exiting...")
@ -238,25 +249,25 @@ class Controller(object):
self.window.notify(title, message) self.window.notify(title, message)
# real functions. These functions really are doing the work. # real functions. These functions really are doing the work.
def search(self, *args, **kwargs): def search(self, text, extractor, *args, **kwargs):
text = self.window.get_text() extractors = get_services()
if text == "":
return
extractor = self.window.extractor.GetValue()
self.change_status(_(u"Searching {0}... ").format(text))
extractors = get_extractors()
for i in extractors: for i in extractors:
if extractor == i.name: if extractor == i.interface.name:
self.extractor = i() self.extractor = i.interface()
break break
log.debug("Started search for {0} (selected extractor: {1})".format(text, self.extractor.name)) log.debug("Started search for {0} (selected extractor: {1})".format(text, self.extractor.name))
self.window.list.Clear() wx.CallAfter(self.window.list.Clear)
self.extractor.search(text) self.extractor.search(text)
self.results = self.extractor.results self.results = self.extractor.results
for i in self.results: for i in self.results:
self.window.list.Append(i.format_track()) wx.CallAfter(self.window.list.Append, i.format_track())
if len(self.results) == 0: if len(self.results) == 0:
self.change_status(_(u"No results found. ")) wx.CallAfter(self.change_status, _(u"No results found. "))
else: else:
self.change_status(u"") wx.CallAfter(self.change_status, u"")
wx.CallAfter(self.window.list.SetFocus) wx.CallAfter(self.window.list.SetFocus)
def reload_extractors(self):
extractors = [i.interface.name for i in get_services()]
self.window.extractor.SetItems(extractors)
self.window.extractor.SetValue(extractors[0])

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals # at top of module
import os import os
import random import random
import vlc
import logging import logging
import config import config
import time
import mpv
from pubsub import pub from pubsub import pub
from utils import call_threaded
player = None player = None
log = logging.getLogger("controller.player") log = logging.getLogger("controller.player")
@ -26,13 +25,26 @@ class audioPlayer(object):
self.stopped = True self.stopped = True
self.queue_pos = 0 self.queue_pos = 0
self.shuffle = False self.shuffle = False
self.instance = vlc.Instance() self.player = mpv.MPV()
self.player = self.instance.media_player_new()
log.debug("Media player instantiated.") # Fires at the end of every file and attempts to play the next one.
self.event_manager = self.player.event_manager() @self.player.event_callback('end-file')
self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback) def handle_end_idle(event):
self.event_manager.event_attach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error) if event.as_dict()["reason"] == b"aborted" or event.as_dict()["reason"] == b"stop":
log.debug("Bound media playback events.") return
log.debug("Reached end of file stream.")
if len(self.queue) > 1:
log.debug("Requesting next item...")
self.next()
def get_output_devices(self):
""" Retrieve enabled output devices so we can switch or use those later. """
return None
def set_output_device(self, device_name):
""" Set Output device to be used in LibVLC"""
log.debug("Setting output audio device to {device}...".format(device=device_name,))
# config.app["main"]["output_device"] = "Default"
def play(self, item): def play(self, item):
self.stopped = True self.stopped = True
@ -41,16 +53,8 @@ class audioPlayer(object):
if item.download_url == "": if item.download_url == "":
item.get_download_url() item.get_download_url()
log.debug("playing {0}...".format(item.download_url,)) log.debug("playing {0}...".format(item.download_url,))
self.stream_new = self.instance.media_new(item.download_url) self.player.play(item.download_url)
self.player.set_media(self.stream_new) self.player.volume = self.vol
if self.player.play() == -1:
log.debug("Error when playing the file {0}".format(item.title,))
pub.sendMessage("change_status", status=_("Error playing {0}. {1}.").format(item.title, e.description))
self.stopped = True
self.is_working = False
self.next()
return
self.player.audio_set_volume(self.vol)
pub.sendMessage("change_status", status=_("Playing {0}.").format(item.title)) pub.sendMessage("change_status", status=_("Playing {0}.").format(item.title))
self.stopped = False self.stopped = False
self.is_working = False self.is_working = False
@ -82,7 +86,7 @@ class audioPlayer(object):
self.stopped = True self.stopped = True
def pause(self): def pause(self):
self.player.pause() self.player.pause = True
if self.stopped == True: if self.stopped == True:
self.stopped = False self.stopped = False
else: else:
@ -97,7 +101,8 @@ class audioPlayer(object):
if vol <= 100 and vol >= 0: if vol <= 100 and vol >= 0:
config.app["main"]["volume"] = vol config.app["main"]["volume"] = vol
self.vol = vol self.vol = vol
self.player.audio_set_volume(self.vol) if self.player != None:
self.player.volume = self.vol
def play_all(self, list_of_items, playing=0, shuffle=False): def play_all(self, list_of_items, playing=0, shuffle=False):
if list_of_items != self.queue: if list_of_items != self.queue:
@ -105,41 +110,3 @@ class audioPlayer(object):
self.shuffle = shuffle self.shuffle = shuffle
self.queue_pos = playing self.queue_pos = playing
self.play(self.queue[self.queue_pos]) self.play(self.queue[self.queue_pos])
def end_callback(self, event, *args, **kwargs):
#https://github.com/ZeBobo5/Vlc.DotNet/issues/4
call_threaded(self.next)
def transcode_audio(self, item, path):
""" Converts given item to mp3. This method will be available when needed automatically."""
if item.download_url == "":
item.get_download_url()
log.debug("Download started: filename={0}, url={1}".format(path, item.download_url))
temporary_filename = "chunk_{0}".format(random.randint(0,2000000))
temporary_path = os.path.join(os.path.dirname(path), temporary_filename)
# Let's get a new VLC instance for transcoding this file.
transcoding_instance = vlc.Instance(*["--sout=#transcode{acodec=mp3,ab=192}:file{mux=raw,dst=\"%s\"}"% (temporary_path,)])
transcoder = transcoding_instance.media_player_new()
transcoder.set_mrl(item.download_url)
pub.sendMessage("change_status", status=_(u"Downloading {0}.").format(item.title,))
media = transcoder.get_media()
transcoder.play()
while True:
state = media.get_state()
pub.sendMessage("change_status", status=_("Downloading {0} ({1}%).").format(item.title, int(transcoder.get_position()*100)))
if str(state) == 'State.Ended':
break
elif str(state) == 'state.error':
os.remove(temporary_path)
break
transcoder.release()
os.rename(temporary_path, path)
log.debug("Download finished sucsessfully.")
pub.sendMessage("download_finished", file=os.path.basename(path))
def playback_error(self, event):
pub.sendMessage("notify", title=_("Error"), message=_("There was an error while trying to access the file you have requested."))
def __del__(self):
self.event_manager.event_detach(vlc.EventType.MediaPlayerEndReached)
self.event_manager.event_detach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error)

View File

@ -1,22 +0,0 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from __future__ import unicode_literals # at top of module
class song(object):
""" Represents a song in all services. Data will be filled by the service itself"""
def __init__(self, extractor):
self.extractor = extractor
self.bitrate = 0
self.title = ""
self.artist = ""
self.duration = ""
self.size = 0
self.url = ""
self.download_url = ""
def format_track(self):
return self.extractor.format_track(self)
def get_download_url(self):
self.download_url = self.extractor.get_download_url(self.url)

View File

@ -1,56 +0,0 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from __future__ import unicode_literals # at top of module
try:
import urllib.parse as urlparse
except ImportError:
import urllib as urlparse
import requests
import youtube_dl
import logging
from bs4 import BeautifulSoup
from . import baseFile
log = logging.getLogger("extractors.mail.ru")
class interface(object):
name = "mail.ru"
def __init__(self):
self.results = []
self.needs_transcode = False
log.debug("Started extraction service for mail.ru music")
def search(self, text, page=1):
if text == "" or text == None:
raise ValueError("Text must be passed and should not be blank.")
site = 'https://my.mail.ru/music/search/%s' % (text)
log.debug("Retrieving data from {0}...".format(site,))
r = requests.get(site)
soup = BeautifulSoup(r.text, 'html.parser')
search_results = soup.find_all("div", {"class": "songs-table__row__col songs-table__row__col--title title songs-table__row__col--title-hq-similar resize"})
self.results = []
for search in search_results:
data = search.find_all("a")
s = baseFile.song(self)
s.title = data[0].text.replace("\n", "").replace("\t", "")
# s.artist = data[1].text.replace("\n", "").replace("\t", "")
# print(data)
s.url = u"https://my.mail.ru"+urlparse.quote(data[0].__dict__["attrs"]["href"].encode("utf-8"))
self.results.append(s)
log.debug("{0} results found.".format(len(self.results)))
def get_download_url(self, url):
log.debug("Getting download URL for {0}".format(url,))
ydl = youtube_dl.YoutubeDL({'quiet': True, 'no_warnings': True, 'logger': log, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
with ydl:
result = ydl.extract_info(url, download=False)
if 'entries' in result:
video = result['entries'][0]
else:
video = result
log.debug("Download URL: {0}".format(video["url"],))
return video["url"]
def format_track(self, item):
return item.title

View File

@ -1,102 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals # at top of module
import isodate
import youtube_dl
import logging
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from .import baseFile
from update.utils import seconds_to_string
DEVELOPER_KEY = "AIzaSyCU_hvZJEjLlAGAnlscquKEkE8l0lVOfn0"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"
log = logging.getLogger("extractors.youtube.com")
class interface(object):
name = "youtube"
def __init__(self):
self.results = []
self.needs_transcode = True
log.debug("started extraction service for {0}".format(self.name,))
def search(self, text, page=1):
if text == "" or text == None:
raise ValueError("Text must be passed and should not be blank.")
if text.startswith("https") or text.startswith("http"):
return self.search_from_url(text)
type = "video"
max_results = 20
log.debug("Retrieving data from Youtube...")
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)
search_response = youtube.search().list(q=text, part="id,snippet", maxResults=max_results, type=type).execute()
self.results = []
ids = []
for search_result in search_response.get("items", []):
if search_result["id"]["kind"] == "youtube#video":
s = baseFile.song(self)
s.title = search_result["snippet"]["title"]
ids.append(search_result["id"]["videoId"])
s.url = "https://www.youtube.com/watch?v="+search_result["id"]["videoId"]
self.results.append(s)
ssr = youtube.videos().list(id=",".join(ids), part="contentDetails", maxResults=1).execute()
for i in range(len(self.results)):
self.results[i].duration = seconds_to_string(isodate.parse_duration(ssr["items"][i]["contentDetails"]["duration"]).total_seconds())
log.debug("{0} results found.".format(len(self.results)))
def search_from_url(self, url):
log.debug("Getting download URL for {0}".format(url,))
if "playlist?list=" in url:
return self.search_from_playlist(url)
ydl = youtube_dl.YoutubeDL({'quiet': True, 'no_warnings': True, 'logger': log, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
with ydl:
result = ydl.extract_info(url, download=False)
if 'entries' in result:
videos = result['entries']
else:
videos = [result]
for video in videos:
s = baseFile.song(self)
s.title = video["title"]
s.url = video["webpage_url"] # Cannot use direct URL here cause Youtube URLS expire after a minute.
s.duration = seconds_to_string(video["duration"])
self.results.append(s)
log.debug("{0} results found.".format(len(self.results)))
def search_from_playlist(self, url):
id = url.split("=")[1]
max_results = 50
log.debug("Retrieving data from Youtube...")
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)
search_response = youtube.playlistItems().list(playlistId=id, part="id, status, snippet", maxResults=max_results).execute()
self.results = []
ids = []
for search_result in search_response.get("items", []):
if search_result["status"]["privacyStatus"] != "public":
continue
s = baseFile.song(self)
s.title = search_result["snippet"]["title"]
ids.append(search_result["snippet"]["resourceId"]["videoId"])
s.url = "https://www.youtube.com/watch?v="+search_result["snippet"]["resourceId"]["videoId"]
self.results.append(s)
ssr = youtube.videos().list(id=",".join(ids), part="contentDetails", maxResults=50).execute()
for i in range(len(self.results)):
self.results[i].duration = seconds_to_string(isodate.parse_duration(ssr["items"][i]["contentDetails"]["duration"]).total_seconds())
log.debug("{0} results found.".format(len(self.results)))
def get_download_url(self, url):
log.debug("Getting download URL for {0}".format(url,))
ydl = youtube_dl.YoutubeDL({'quiet': True, 'no_warnings': True, 'logger': log, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
with ydl:
result = ydl.extract_info(url, download=False)
if 'entries' in result:
video = result['entries'][0]
else:
video = result
log.debug("Download URL: {0}".format(video["url"],))
return video["url"]
def format_track(self, item):
return "{0} {1}".format(item.title, item.duration)

View File

@ -1,52 +0,0 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from __future__ import unicode_literals # at top of module
import re
import json
import requests
import logging
from bs4 import BeautifulSoup
from . import baseFile
log = logging.getLogger("extractors.zaycev.net")
class interface(object):
name = "zaycev.net"
def __init__(self):
self.results = []
self.needs_transcode = False
log.debug("Started extraction service for zaycev.net")
def search(self, text, page=1):
if text == "" or text == None:
raise ValueError("Text must be passed and should not be blank.")
site = "http://zaycev.net/search.html?query_search=%s" % (text,)
log.debug("Retrieving data from {0}...".format(site,))
r = requests.get(site)
soup = BeautifulSoup(r.text, 'html.parser')
search_results = soup.find_all("div", {"class": "musicset-track__title track-geo__title"})
self.results = []
for i in search_results:
# The easiest method to get artist and song names is to fetch links. There are only two links per result here.
data = i.find_all("a")
# from here, data[0] contains artist info and data[1] contains info of the retrieved song.
s = baseFile.song(self)
s.title = data[1].text
s.artist = data[0].text
s.url = "http://zaycev.net%s" % (data[1].attrs["href"])
# s.duration = self.hd[i]["duration"]
# s.size = self.hd[i]["size"]
# s.bitrate = self.hd[i]["bitrate"]
self.results.append(s)
log.debug("{0} results found.".format(len(self.results)))
def get_download_url(self, url):
log.debug("Getting download URL for {0}".format(url,))
soups = BeautifulSoup(requests.get(url).text, 'html.parser')
data = json.loads(requests.get('http://zaycev.net' + soups.find('div', {'class':"musicset-track"}).get('data-url')).text)
log.debug("Download URL: {0}".format(data["url"]))
return data["url"]
def format_track(self, item):
return "{0}. {1}. {2}".format(item.title, item.duration, item.size)

View File

@ -1,11 +1,12 @@
!include "MUI2.nsh" !include "MUI2.nsh"
!include "LogicLib.nsh" !include "LogicLib.nsh"
!include "x64.nsh"
Unicode true Unicode true
CRCCheck on CRCCheck on
ManifestSupportedOS all ManifestSupportedOS all
XPStyle on XPStyle on
Name "MusicDL" Name "MusicDL"
OutFile "music_dl_0.5_setup.exe" OutFile "music_dl_setup.exe"
InstallDir "$PROGRAMFILES\musicDL" InstallDir "$PROGRAMFILES\musicDL"
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "InstallLocation" InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "InstallLocation"
RequestExecutionLevel admin RequestExecutionLevel admin
@ -13,18 +14,18 @@ SetCompress auto
SetCompressor /solid lzma SetCompressor /solid lzma
SetDatablockOptimize on SetDatablockOptimize on
VIAddVersionKey ProductName "MusicDL" VIAddVersionKey ProductName "MusicDL"
VIAddVersionKey LegalCopyright "Copyright 2019 Manuel Cortez." VIAddVersionKey LegalCopyright "Copyright 2019 - 2022 MCV Software."
VIAddVersionKey ProductVersion "0.5" VIAddVersionKey ProductVersion "0.7"
VIAddVersionKey FileVersion "0.5" VIAddVersionKey FileVersion "0.7"
VIProductVersion "0.5.0.0" VIProductVersion "0.7.0.0"
VIFileVersion "0.5.0.0" VIFileVersion "0.7.0.0"
!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_DIRECTORY
var StartMenuFolder var StartMenuFolder
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder !insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_LINK "Visit MusicDL website" !define MUI_FINISHPAGE_LINK "Visit MusicDL website"
!define MUI_FINISHPAGE_LINK_LOCATION "https://manuelcortez.net/music_dl" !define MUI_FINISHPAGE_LINK_LOCATION "https://mcvsoftware.com/music_dl"
!define MUI_FINISHPAGE_RUN "$INSTDIR\musicDL.exe" !define MUI_FINISHPAGE_RUN "$INSTDIR\musicDL.exe"
!insertmacro MUI_PAGE_FINISH !insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_CONFIRM
@ -36,7 +37,11 @@ var StartMenuFolder
Section Section
SetShellVarContext All SetShellVarContext All
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
File /r dist\main\* ${If} ${RunningX64}
File /r program64\*
${Else}
File /r program32\*
${EndIf}
CreateShortCut "$DESKTOP\musicDL.lnk" "$INSTDIR\musicDL.exe" CreateShortCut "$DESKTOP\musicDL.lnk" "$INSTDIR\musicDL.exe"
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu !insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
CreateDirectory "$SMPROGRAMS\$StartMenuFolder" CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
@ -49,7 +54,7 @@ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "DisplayVersion" "0.5" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "DisplayVersion" "0.7"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "URLInfoAbout" "https://manuelcortez.net/music_dl" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "URLInfoAbout" "https://manuelcortez.net/music_dl"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "VersionMajor" 0 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "VersionMajor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "VersionMinor" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\musicDL" "VersionMinor" 1
@ -65,5 +70,8 @@ Delete "$DESKTOP\MusicDL.lnk"
RMDir /r "$SMPROGRAMS\$StartMenuFolder" RMDir /r "$SMPROGRAMS\$StartMenuFolder"
SectionEnd SectionEnd
Function .onInit Function .onInit
${If} ${RunningX64}
StrCpy $instdir "$programfiles64\musicDL"
${EndIf}
!insertmacro MUI_LANGDLL_DISPLAY !insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd FunctionEnd

Binary file not shown.

Binary file not shown.

View File

@ -1,237 +1,20 @@
# SOME DESCRIPTIVE TITLE. # SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION # Copyright (C) 2019 ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, 2019.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2018-02-28 15:02-0600\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"PO-Revision-Date: 2018-03-17 16:25-0600\n" "POT-Creation-Date: 2020-12-29 05:08-0800\n"
"Last-Translator: \n" "PO-Revision-Date: 2020-10-02 15:10+0000\n"
"Language-Team: \n" "Last-Translator: Manuel Cortez <manuel@manuelcortez.net>\n"
"Language: es\n" "Language: es\n"
"Language-Team: Spanish "
"<http://translations.manuelcortez.net/projects/musicdl/interface/es/>\n"
"Plural-Forms: nplurals=2; plural=n != 1\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: Babel 2.9.0\n"
"X-Generator: Poedit 2.0.2\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../src\application.py:7
msgid ""
" Is an application that will allow you to download music from popular sites "
"such as youtube, zaycev.net."
msgstr ""
" Es una aplicación que te permite descargar música de sitios populares como "
"YouTube y zaycev.net."
#: ../src\application.py:12
msgid "Manuel Cortez (Spanish)"
msgstr "Manuel Cortez (Español)"
#: ../src\controller\mainController.py:27
msgid "Ready"
msgstr "Listo"
#: ../src\controller\mainController.py:42
msgid "Showing {0} results."
msgstr "Mostrando {0} resultados"
#: ../src\controller\mainController.py:46
msgid "Shuffle on"
msgstr "Modo aleatorio activo"
#: ../src\controller\mainController.py:105
#: ../src\controller\mainController.py:125 ../src\wxUI\mainWindow.py:13
#: ../src\wxUI\mainWindow.py:61
msgid "Play"
msgstr "Reproducir"
#: ../src\controller\mainController.py:108
#: ../src\controller\mainController.py:120
msgid "Pause"
msgstr "pausa"
#: ../src\controller\mainController.py:206
msgid "Searching {0}... "
msgstr "Buscando {0}..."
#: ../src\controller\player.py:42
msgid "Error playing {0}. {1}."
msgstr "Error reproduciendo {0}. {1}."
#: ../src\controller\player.py:48
msgid "Playing {0}."
msgstr "Reproduciendo {0}."
#: ../src\controller\player.py:116 ../src\utils.py:53
msgid "Downloading {0}."
msgstr "Descargando {0}."
#: ../src\controller\player.py:121 ../src\utils.py:63
msgid "Downloading {0} ({1}%)."
msgstr "Descargando {0} ({1}%)."
#: ../src\update\utils.py:27
msgid "%d day, "
msgstr "%d día, "
#: ../src\update\utils.py:29
msgid "%d days, "
msgstr "%d días, "
#: ../src\update\utils.py:31
msgid "%d hour, "
msgstr "%d hora, "
#: ../src\update\utils.py:33
msgid "%d hours, "
msgstr "%d horas, "
#: ../src\update\utils.py:35
msgid "%d minute, "
msgstr "%d minuto, "
#: ../src\update\utils.py:37
msgid "%d minutes, "
msgstr "%d minutos, "
#: ../src\update\utils.py:39
msgid "%s second"
msgstr "%s segundo"
#: ../src\update\utils.py:41
msgid "%s seconds"
msgstr "%s segundos"
#: ../src\update\wxUpdater.py:9
msgid "New version for %s"
msgstr "Nueva versión de %s"
#: ../src\update\wxUpdater.py:9
msgid ""
"There's a new %s version available. Would you like to download it now?\n"
"\n"
" %s version: %s\n"
"\n"
"Changes:\n"
"%s"
msgstr ""
"Hay una nueva versión de %s disponible. ¿Te gustaría descargarla ahora?\n"
"\n"
" %s versión: %s\n"
"\n"
"Novedades:\n"
"%s"
#: ../src\update\wxUpdater.py:16
msgid "Download in Progress"
msgstr "Descarga en progreso"
#: ../src\update\wxUpdater.py:16
msgid "Downloading the new version..."
msgstr "Descargando la nueva versión..."
#: ../src\update\wxUpdater.py:26
msgid "Updating... %s of %s"
msgstr "Actualizando... %s de %s"
#: ../src\update\wxUpdater.py:29
msgid "Done!"
msgstr "¡Hecho!"
#: ../src\update\wxUpdater.py:29
msgid ""
"The update has been downloaded and installed successfully. Press OK to "
"continue."
msgstr ""
"La actualización ha sido descargada e instalada satisfactoriamente. Pulsa "
"aceptar para continuar."
#: ../src\wxUI\mainWindow.py:14 ../src\wxUI\mainWindow.py:62
msgid "Stop"
msgstr "Detener"
#: ../src\wxUI\mainWindow.py:15 ../src\wxUI\mainWindow.py:60
msgid "Previous"
msgstr "Anterior"
#: ../src\wxUI\mainWindow.py:16 ../src\wxUI\mainWindow.py:63
msgid "Next"
msgstr "Siguiente"
#: ../src\wxUI\mainWindow.py:17
msgid "Shuffle"
msgstr "Aleatorio"
#: ../src\wxUI\mainWindow.py:18
msgid "Volume down"
msgstr "Bajar volumen"
#: ../src\wxUI\mainWindow.py:19
msgid "Volume up"
msgstr "Subir volumen"
#: ../src\wxUI\mainWindow.py:20
msgid "Mute"
msgstr "Silenciar"
#: ../src\wxUI\mainWindow.py:22
msgid "About {0}"
msgstr "Sobre {0}"
#: ../src\wxUI\mainWindow.py:23
msgid "Check for updates"
msgstr "Comprobar actualizaciones"
#: ../src\wxUI\mainWindow.py:24
msgid "Visit website"
msgstr "Visitar sitio web"
#: ../src\wxUI\mainWindow.py:25
msgid "Player"
msgstr "Reproductor"
#: ../src\wxUI\mainWindow.py:26
msgid "Help"
msgstr "Ayuda"
#: ../src\wxUI\mainWindow.py:36
msgid "search"
msgstr "Buscar"
#: ../src\wxUI\mainWindow.py:41
msgid "Search in"
msgstr "Buscar en"
#: ../src\wxUI\mainWindow.py:44
msgid "Search"
msgstr "Buscar"
#: ../src\wxUI\mainWindow.py:48
msgid "Results"
msgstr "Resultados"
#: ../src\wxUI\mainWindow.py:54
msgid "Position"
msgstr "Posición"
#: ../src\wxUI\mainWindow.py:57
msgid "Volume"
msgstr "Volumen"
#: ../src\wxUI\mainWindow.py:99
msgid "Audio Files(*.mp3)|*.mp3"
msgstr "Archivos de audio (*.mp3)|*.mp3"
#: ../src\wxUI\mainWindow.py:99
msgid "Save this file"
msgstr "Guardar archivo"
#: ../src\wxUI\menus.py:7
msgid "Play/Pause"
msgstr "Reproducir/pausar"
#: ../src\wxUI\menus.py:9
msgid "Download"
msgstr "Descargar"

View File

@ -1,254 +1,20 @@
# SOME DESCRIPTIVE TITLE. # SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION # Copyright (C) 2018 ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: \n"
"POT-Creation-Date: 2018-03-03 09:38+Hora estándar central (México)\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"PO-Revision-Date: 2018-03-12 01:35+0400\n" "POT-Creation-Date: 2020-12-29 05:08-0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "PO-Revision-Date: 2020-07-30 05:13-0500\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Last-Translator: Manuel Cortez <manuel@manuelcortez.net>\n"
"Language: ru\n"
"Language-Team: ru <LL@li.org>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: Babel 2.9.0\n"
"X-Generator: Poedit 1.5.7\n"
#: ../src\application.py:7
msgid ""
" Is an application that will allow you to download music from popular sites "
"such as youtube, zaycev.net."
msgstr ""
" Это приложение, позволяющее скачивать музыку с таких популярных сайтов как "
"Youtube, zaycev.net."
#: ../src\application.py:12
msgid "Manuel Cortez (Spanish)"
msgstr "Manuel Cortez (Испанский)"
#: ../src\controller\mainController.py:27
msgid "Ready"
msgstr "Готово"
#: ../src\controller\mainController.py:42
msgid "Showing {0} results."
msgstr "Показано {0} результатов."
#: ../src\controller\mainController.py:46
msgid "Shuffle on"
msgstr "Случайный порядок включён"
#: ../src\controller\mainController.py:118
#: ../src\controller\mainController.py:138 ../src\wxUI\mainWindow.py:13
#: ../src\wxUI\mainWindow.py:62
msgid "Play"
msgstr "Воспроизвести"
#: ../src\controller\mainController.py:121
#: ../src\controller\mainController.py:133
msgid "Pause"
msgstr "Приостановить"
#: ../src\controller\mainController.py:213
msgid "File downloaded: {0}"
msgstr "Файл загружен: {0}"
#: ../src\controller\mainController.py:236
msgid "Searching {0}... "
msgstr "Поиск {0}... "
#: ../src\controller\mainController.py:242
msgid "No results found. "
msgstr "Нет результатов."
#: ../src\controller\player.py:43
msgid "Error playing {0}. {1}."
msgstr "Ошибка воспроизведения {0}. {1}."
#: ../src\controller\player.py:49
msgid "Playing {0}."
msgstr "Сейчас играет {0}."
#: ../src\controller\player.py:117 ../src\utils.py:53
msgid "Downloading {0}."
msgstr "Сейчас загружается {0}."
#: ../src\controller\player.py:122 ../src\utils.py:63
msgid "Downloading {0} ({1}%)."
msgstr "Загрузка {0} ({1}%)."
#: ../src\controller\player.py:133
msgid "Error"
msgstr "Ошибка"
#: ../src\controller\player.py:133
msgid "There was an error while trying to access the file you have requested."
msgstr "Произошла ошибка при попытке открыть запрашиваемый файл."
#: ../src\update\utils.py:27
msgid "%d day, "
msgstr "%d день, "
#: ../src\update\utils.py:29
msgid "%d days, "
msgstr "%d дней, "
#: ../src\update\utils.py:31
msgid "%d hour, "
msgstr "%d час, "
#: ../src\update\utils.py:33
msgid "%d hours, "
msgstr "%d часов, "
#: ../src\update\utils.py:35
msgid "%d minute, "
msgstr "%d минута, "
#: ../src\update\utils.py:37
msgid "%d minutes, "
msgstr "%d минут, "
#: ../src\update\utils.py:39
msgid "%s second"
msgstr "%s секунда"
#: ../src\update\utils.py:41
msgid "%s seconds"
msgstr "%s секунд"
#: ../src\update\wxUpdater.py:9
msgid "New version for %s"
msgstr "Новая версия %s"
#: ../src\update\wxUpdater.py:9
msgid ""
"There's a new %s version available. Would you like to download it now?\n"
"\n"
" %s version: %s\n"
"\n"
"Changes:\n"
"%s"
msgstr ""
"Доступна новая версия %s. Желаете скачать её?\n"
"\n"
" %s версия: %s\n"
"\n"
"Изменения:\n"
"%s"
#: ../src\update\wxUpdater.py:16
msgid "Download in Progress"
msgstr "Процесс скачивания"
#: ../src\update\wxUpdater.py:16
msgid "Downloading the new version..."
msgstr "Скачивание новой версии..."
#: ../src\update\wxUpdater.py:26
msgid "Updating... %s of %s"
msgstr "Обновление... %s из %s"
#: ../src\update\wxUpdater.py:29
msgid "Done!"
msgstr "Готово!"
#: ../src\update\wxUpdater.py:29
msgid ""
"The update has been downloaded and installed successfully. Press OK to "
"continue."
msgstr ""
"Обновление было успешно загружено и установлено. Нажмите ОК для продолжения."
#: ../src\wxUI\mainWindow.py:14 ../src\wxUI\mainWindow.py:63
msgid "Stop"
msgstr "Остановить"
#: ../src\wxUI\mainWindow.py:15 ../src\wxUI\mainWindow.py:61
msgid "Previous"
msgstr "Предыдущая композиция"
#: ../src\wxUI\mainWindow.py:16 ../src\wxUI\mainWindow.py:64
msgid "Next"
msgstr "Следующая композиция"
#: ../src\wxUI\mainWindow.py:17
msgid "Shuffle"
msgstr "Перемешать"
#: ../src\wxUI\mainWindow.py:18
msgid "Volume down"
msgstr "Уменьшить громкость"
#: ../src\wxUI\mainWindow.py:19
msgid "Volume up"
msgstr "Увеличить громкость"
#: ../src\wxUI\mainWindow.py:20
msgid "Mute"
msgstr "Выключить звук"
#: ../src\wxUI\mainWindow.py:22
msgid "About {0}"
msgstr "О {0}"
#: ../src\wxUI\mainWindow.py:23
msgid "Check for updates"
msgstr "Проверить на наличие обновлений"
#: ../src\wxUI\mainWindow.py:24
msgid "What's new in this version?"
msgstr "Что нового в этой версии?"
#: ../src\wxUI\mainWindow.py:25
msgid "Visit website"
msgstr "Посетить вебсайт"
#: ../src\wxUI\mainWindow.py:26
msgid "Player"
msgstr "Плеер"
#: ../src\wxUI\mainWindow.py:27
msgid "Help"
msgstr "Помощь"
#: ../src\wxUI\mainWindow.py:37
msgid "search"
msgstr "Поиск"
#: ../src\wxUI\mainWindow.py:42
msgid "Search in"
msgstr "Искать с помощью"
#: ../src\wxUI\mainWindow.py:45
msgid "Search"
msgstr "Поиск"
#: ../src\wxUI\mainWindow.py:49
msgid "Results"
msgstr "Результаты"
#: ../src\wxUI\mainWindow.py:55
msgid "Position"
msgstr "Позиция"
#: ../src\wxUI\mainWindow.py:58
msgid "Volume"
msgstr "Громкость"
#: ../src\wxUI\mainWindow.py:100
msgid "Audio Files(*.mp3)|*.mp3"
msgstr "Аудио Файлы(*.mp3)|*.mp3"
#: ../src\wxUI\mainWindow.py:100
msgid "Save this file"
msgstr "Сохранить этот файл"
#: ../src\wxUI\menus.py:7
msgid "Play/Pause"
msgstr "Воспроизвести/Приостановить"
#: ../src\wxUI\menus.py:9
msgid "Download"
msgstr "Скачать"

View File

@ -1,9 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals # at top of module
# this is the first fix we have to import just before the paths module would.
# it changes a call from wintypes to ctypes.
from fixes import fix_winpaths
fix_winpaths.fix()
import os import os
import logging import logging
import storage import storage
@ -12,10 +7,7 @@ import sys
storage.setup() storage.setup()
# Let's import config module here as it is dependent on storage being setup. # Let's import config module here as it is dependent on storage being setup.
import config import config
logging.basicConfig(filename=os.path.join(storage.data_directory, "info.log"), level=logging.DEBUG, filemode="w") logging.basicConfig(handlers=[logging.FileHandler(os.path.join(storage.data_directory, "info.log"), "w", "utf-8")], level=logging.DEBUG)
# Let's mute the google discovery_cache logger as we won't use it and we'll avoid some tracebacks.
glog = logging.getLogger("googleapiclient.discovery_cache")
glog.setLevel(logging.CRITICAL)
# Let's capture all exceptions raised in our log file (especially useful for pyinstaller builds). # Let's capture all exceptions raised in our log file (especially useful for pyinstaller builds).
sys.excepthook = lambda x, y, z: logging.critical(''.join(traceback.format_exception(x, y, z))) sys.excepthook = lambda x, y, z: logging.critical(''.join(traceback.format_exception(x, y, z)))
log = logging.getLogger("main") log = logging.getLogger("main")

View File

@ -1,37 +0,0 @@
# -*- mode: python -*-
block_cipher = None
a = Analysis(['main.py'],
pathex=['.'],
binaries=[("plugins", "plugins"),
("locales", "locales"),
("libvlc.dll", "."),
("libvlccore.dll", "."),
("bootstrap.exe", "."),
("app-configuration.defaults", ".")],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='musicDL',
debug=False,
strip=False,
upx=True,
console=False )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='main')

View File

@ -1,116 +1,70 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals
import inspect import sys
import platform import platform
import os import os
import subprocess import glob
import sys from platform_utils import paths as paths_
import string
import unicodedata
from functools import wraps
def app_data_path(app_name=None): mode = "portable"
"""Cross-platform method for determining where to put application data.""" directory = None
"""Requires the name of the application""" fsencoding = sys.getfilesystemencoding()
plat = platform.system()
if plat == 'Windows':
import winpaths
path = winpaths.get_appdata()
elif plat == 'Darwin':
path = os.path.join(os.path.expanduser('~'), 'Library', 'Application Support')
elif plat == 'Linux':
path = os.path.expanduser('~')
app_name = '.%s' % app_name.replace(' ', '_')
return os.path.join(path, app_name)
def prepare_app_data_path(app_name): if len(glob.glob("Uninstall.exe")) > 0: # installed copy
"""Creates the application's data directory, given its name.""" mode= "installed"
dir = app_data_path(app_name)
return ensure_path(dir)
def embedded_data_path():
if platform.system() == 'Darwin' and is_frozen():
return os.path.abspath(os.path.join(executable_directory(), '..', 'Resources'))
return app_path()
def is_frozen():
"""Return a bool indicating if application is compressed"""
import imp
return hasattr(sys, 'frozen') or imp.is_frozen("__main__")
def get_executable():
"""Returns the full executable path/name if frozen, or the full path/name of the main module if not."""
if is_frozen():
if platform.system() != 'Darwin':
return sys.executable
#On darwin, sys.executable points to python. We want the full path to the exe we ran.
exedir = os.path.abspath(os.path.dirname(sys.executable))
items = os.listdir(exedir)
items.remove('python')
return os.path.join(exedir, items[0])
#Not frozen
try:
import __main__
return os.path.abspath(__main__.__file__)
except AttributeError:
return sys.argv[0]
def get_module(level=2):
"""Hacky method for deriving the caller of this function's module."""
return inspect.getmodule(inspect.stack()[level][0]).__file__
def executable_directory():
"""Always determine the directory of the executable, even when run with py2exe or otherwise frozen"""
executable = get_executable()
path = os.path.abspath(os.path.dirname(executable))
return path
def app_path(): def app_path():
"""Return the root of the application's directory""" return paths_.app_path()
path = executable_directory()
if is_frozen() and platform.system() == 'Darwin':
path = os.path.abspath(os.path.join(path, '..', '..'))
return path
def module_path(level=2): def config_path():
return os.path.abspath(os.path.dirname(get_module(level))) global mode, directory
if mode == "portable":
def documents_path(): if directory != None: path = os.path.join(directory, "config")
"""On windows, returns the path to My Documents. On OSX, returns the user's Documents folder. For anything else, returns the user's home directory.""" elif directory == None: path = os.path.join(app_path(), "config")
plat = platform.system() elif mode == "installed":
if plat == 'Windows': path = os.path.join(data_path(), "config")
import winpaths
path = winpaths.get_my_documents()
elif plat == 'Darwin':
path = os.path.join(os.path.expanduser('~'), 'Documents')
else:
path = os.path.expanduser('~')
return path
def safe_filename(filename):
"""Given a filename, returns a safe version with no characters that would not work on different platforms."""
SAFE_FILE_CHARS = "'-_.()[]{}!@#$%^&+=`~ "
filename = unicode(filename)
new_filename = ''.join(c for c in filename if c in SAFE_FILE_CHARS or c.isalnum())
#Windows doesn't like directory names ending in space, macs consider filenames beginning with a dot as hidden, and windows removes dots at the ends of filenames.
return new_filename.strip(' .')
def ensure_path(path):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) # log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path)
return path return path
def start_file(path): def logs_path():
if platform.system() == 'Windows': global mode, directory
os.startfile(path) if mode == "portable":
else: if directory != None: path = os.path.join(directory, "logs")
subprocess.Popen(['open', path]) elif directory == None: path = os.path.join(app_path(), "logs")
elif mode == "installed":
path = os.path.join(data_path(), "logs")
if not os.path.exists(path):
# log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path)
return path
def get_applications_path(): def data_path(app_name='socializer'):
"""Return the directory where applications are commonly installed on the system.""" if platform.system() == "Windows":
plat = platform.system() data_path = os.path.join(os.getenv("AppData"), app_name)
if plat == 'Windows': else:
import winpaths data_path = os.path.join(os.environ['HOME'], ".%s" % app_name)
return winpaths.get_program_files() if not os.path.exists(data_path):
elif plat == 'Darwin': os.mkdir(data_path)
return '/Applications' return data_path
def locale_path():
return os.path.join(app_path(), "locales")
def sound_path():
return os.path.join(app_path(), "sounds")
def com_path():
global mode, directory
if mode == "portable":
if directory != None: path = os.path.join(directory, "com_cache")
elif directory == None: path = os.path.join(app_path(), "com_cache")
elif mode == "installed":
path = os.path.join(data_path(), "com_cache")
if not os.path.exists(path):
# log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path)
return path

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More