Skip to content

Commit

Permalink
Merge pull request #5 from sentenzo/dev
Browse files Browse the repository at this point in the history
Regular `dev` merge
  • Loading branch information
sentenzo authored Sep 26, 2022
2 parents 491df7c + 988fca4 commit bbbed49
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ __pycache__
.venv
.venv_linux
.venv_windows
bin
/bin

.pyinstaller

Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ build: pyui
--name $(PACKAGE_NAME) \
--icon ../$(PACKAGE_NAME)/rec/ico/file-video.ico \
--add-data ../$(PACKAGE_NAME)/rec/ico/*$(PATH_ARG_SEP)./$(PACKAGE_NAME)/rec/ico \
--add-data ../$(PACKAGE_NAME)/thirdparty/ffmpeg/bin/*$(PATH_ARG_SEP)./$(PACKAGE_NAME)/thirdparty/ffmpeg/bin \
$(PACKAGE_NAME)/__main__.py

run:
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@


# tubic
<h1>tubic</h1>
<div align="center">

![Screenshot](tubic.webp)

</div>
A simple lightweight portable single-file program to download videos from YouTube.

A simple lightweight portable single-file program to download video or audio (as `.mp3`) from YouTube.

---

- [Installation](#installation)
- [Backstory](#backstory)
- [Building](#building)
- [Software requirements](#software-requirements)
- [First run](#first-run)
- [GUI editing](#gui-editing)
- [Basic `make` targets](#basic-make-targets)
- [Software requirements](#software-requirements)
- [First run](#first-run)
- [GUI editing](#gui-editing)
- [Basic `make` targets](#basic-make-targets)
- [Licensing](#licensing)

## Installation
Expand Down Expand Up @@ -45,13 +46,13 @@ I've been trying out several tools of that kind, and each one of them had some f

I feel an unfulfilled demand for a very simple tool, which would:
- -be able to download both video and audio by a YouTube link
- -download audios as `.mp3`-files
- -have a simple GUI (the less buttons — the better)
- -be portable (ideally: packed in a single executable)

The features I (subjectively) consider redundant:
- sequential download from a list of links
- post-processing (`video: 1080p → 780p` or `audio: 320 kbps → 96 kbps`)
- converting formats (`mp4 → mkv` or `webm → mp3`)

So that's how I've decided to make this stuff.

Expand Down Expand Up @@ -100,3 +101,4 @@ Qt Designer related files can be found at `tubic/qt_wrap/ui`.

- Current repository is licensed under [MIT License](https://github.com/sentenzo/tubic/blob/master/LICENSE)
- The icons for this application were taken from paomedia's [small-n-flat](https://github.com/paomedia/small-n-flat) set and licensed under [CC0 1.0 Universal](https://github.com/paomedia/small-n-flat/blob/master/LICENSE)
- [ffmpeg](https://github.com/FFmpeg/FFmpeg) is licensed under a [mixed license](https://github.com/FFmpeg/FFmpeg/blob/master/LICENSE.md), compatible with MIT License
6 changes: 3 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions tubic/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
if __name__ == "__main__":
import tubic.app_gui as app
import tubic.app_gui as app

app.run()
app.run()
21 changes: 0 additions & 21 deletions tubic/qt_wrap/helpers.py

This file was deleted.

16 changes: 14 additions & 2 deletions tubic/qt_wrap/py/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@
import PyQt6.QtCore as qtc

from tubic.qt_wrap.pyui.main_window import Ui_MainWindow
import tubic.qt_wrap.helpers as helpers

from tubic.utils import fix_path


def _getWindowIcon() -> qtg.QIcon:
rec_file = fix_path("tubic/rec/ico/file-video-{0}.png")
app_icon = qtg.QIcon()
app_icon.addFile(rec_file.format(24), qtc.QSize(24, 24))
app_icon.addFile(rec_file.format(48), qtc.QSize(48, 48))
app_icon.addFile(rec_file.format(72), qtc.QSize(72, 72))
app_icon.addFile(rec_file.format(96), qtc.QSize(96, 96))

return app_icon


class MainWindowBase(qtw.QMainWindow):
Expand All @@ -17,7 +29,7 @@ def __init__(self, parent=None) -> None:
Ui_MainWindow().setupUi(self)
self.setFocus()
self.setFixedSize(self.size())
self.setWindowIcon(helpers.getWindowIcon())
self.setWindowIcon(_getWindowIcon())

self.status_line_descriptor = (
"-" * 11
Expand Down
9 changes: 4 additions & 5 deletions tubic/qt_wrap/tubic_main_window.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from concurrent.futures import thread
import PyQt6.QtWidgets as qtw
import PyQt6.QtGui as qtg
import PyQt6.QtCore as qtc

from tubic.qt_wrap.py.main_window import MainWindowBase
from tubic.qt_wrap.workers import DownloadVideoWorker, DownloadThumbnailWorker
Expand All @@ -15,11 +12,13 @@ def __init__(self, parent=None) -> None:
self.yt_link_wrap: LinkWrapper = LinkWrapper.get_dummy()

self.pb_download_video.clicked.connect(
lambda: self.try_download(self.yt_link_wrap, hide=self.pb_download_video)
lambda: self.try_download(
self.yt_link_wrap.format_sort(["res:720"]), hide=self.pb_download_video
)
)
self.pb_download_audio.clicked.connect(
lambda: self.try_download(
self.yt_link_wrap.audio(), hide=self.pb_download_audio
self.yt_link_wrap.mp3(), hide=self.pb_download_audio
)
)
self.pb_abort_download.clicked.connect(self.abort_one_worker)
Expand Down
30 changes: 22 additions & 8 deletions tubic/qt_wrap/workers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# from abc import ABC, abstractmethod
import PyQt6.QtCore as qtc
import PyQt6.QtGui as qtg

Expand Down Expand Up @@ -64,6 +63,12 @@ def abort(self):

@classmethod
def create_thread(cls, window: MainWindowBase, link_wrap_obj: LinkWrapper):
def abortion_check(status_line: str):
if window._abort_one_worker:
window._abort_one_worker = False
window.set_status_line(status_line)
raise WorkerAborted(status_line)

def progress_bar_pseudo_graphic(value: int, total: int) -> str:
pb_str_len = 30 # the length of the pseudo graphic progress bar
full, empty = "■", "□"
Expand All @@ -73,12 +78,8 @@ def progress_bar_pseudo_graphic(value: int, total: int) -> str:
empty_count = pb_str_len - full_count
return full * full_count + empty * empty_count

def progress_hook(msg):
nonlocal window
if window._abort_one_worker:
window._abort_one_worker = False
window.set_status_line(f"download was aborted")
raise WorkerAborted("Download was aborted")
def p_hook(msg):
abortion_check("download was aborted")
status = msg["status"]
if status == "downloading":
downloaded = msg["downloaded_bytes"]
Expand All @@ -92,7 +93,20 @@ def progress_hook(msg):
pbpg = progress_bar_pseudo_graphic(100, 100)
window.set_status_line(f"finished - {pbpg} - 100.00%")

link_wrap_obj = link_wrap_obj.progress_hook(progress_hook)
def pp_hook(msg):
abortion_check("post-processing was aborted")
status = msg["status"]
postprocessor = msg["postprocessor"]
status_lines = {
("started", "ExtractAudio"): "extracting audio",
("started", "MoveFiles"): "moving files",
("finished", "MoveFiles"): "finished",
}
status_line = status_lines.get((status, postprocessor), None)
if status_line:
window.set_status_line(status_line)

link_wrap_obj = link_wrap_obj.progress_hook(p_hook).postprocessor_hook(pp_hook)

thread = super().create_thread(window, link_wrap_obj)
return thread
Expand Down
129 changes: 129 additions & 0 deletions tubic/thirdparty/ffmpeg/bin/LICENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# License

Most files in FFmpeg are under the GNU Lesser General Public License version 2.1
or later (LGPL v2.1+). Read the file `COPYING.LGPLv2.1` for details. Some other
files have MIT/X11/BSD-style licenses. In combination the LGPL v2.1+ applies to
FFmpeg.

Some optional parts of FFmpeg are licensed under the GNU General Public License
version 2 or later (GPL v2+). See the file `COPYING.GPLv2` for details. None of
these parts are used by default, you have to explicitly pass `--enable-gpl` to
configure to activate them. In this case, FFmpeg's license changes to GPL v2+.

Specifically, the GPL parts of FFmpeg are:

- libpostproc
- optional x86 optimization in the files
- `libavcodec/x86/flac_dsp_gpl.asm`
- `libavcodec/x86/idct_mmx.c`
- `libavfilter/x86/vf_removegrain.asm`
- the following building and testing tools
- `compat/solaris/make_sunver.pl`
- `doc/t2h.pm`
- `doc/texi2pod.pl`
- `libswresample/tests/swresample.c`
- `tests/checkasm/*`
- `tests/tiny_ssim.c`
- the following filters in libavfilter:
- `signature_lookup.c`
- `vf_blackframe.c`
- `vf_boxblur.c`
- `vf_colormatrix.c`
- `vf_cover_rect.c`
- `vf_cropdetect.c`
- `vf_delogo.c`
- `vf_eq.c`
- `vf_find_rect.c`
- `vf_fspp.c`
- `vf_histeq.c`
- `vf_hqdn3d.c`
- `vf_kerndeint.c`
- `vf_lensfun.c` (GPL version 3 or later)
- `vf_mcdeint.c`
- `vf_mpdecimate.c`
- `vf_nnedi.c`
- `vf_owdenoise.c`
- `vf_perspective.c`
- `vf_phase.c`
- `vf_pp.c`
- `vf_pp7.c`
- `vf_pullup.c`
- `vf_repeatfields.c`
- `vf_sab.c`
- `vf_signature.c`
- `vf_smartblur.c`
- `vf_spp.c`
- `vf_stereo3d.c`
- `vf_super2xsai.c`
- `vf_tinterlace.c`
- `vf_uspp.c`
- `vf_vaguedenoiser.c`
- `vsrc_mptestsrc.c`

Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then
the configure parameter `--enable-version3` will activate this licensing option
for you. Read the file `COPYING.LGPLv3` or, if you have enabled GPL parts,
`COPYING.GPLv3` to learn the exact legal terms that apply in this case.

There are a handful of files under other licensing terms, namely:

* The files `libavcodec/jfdctfst.c`, `libavcodec/jfdctint_template.c` and
`libavcodec/jrevdct.c` are taken from libjpeg, see the top of the files for
licensing details. Specifically note that you must credit the IJG in the
documentation accompanying your program if you only distribute executables.
You must also indicate any changes including additions and deletions to
those three files in the documentation.
* `tests/reference.pnm` is under the expat license.


## External libraries

FFmpeg can be combined with a number of external libraries, which sometimes
affect the licensing of binaries resulting from the combination.

### Compatible libraries

The following libraries are under GPL version 2:
- avisynth
- frei0r
- libcdio
- libdavs2
- librubberband
- libvidstab
- libx264
- libx265
- libxavs
- libxavs2
- libxvid

When combining them with FFmpeg, FFmpeg needs to be licensed as GPL as well by
passing `--enable-gpl` to configure.

The following libraries are under LGPL version 3:
- gmp
- libaribb24
- liblensfun

When combining them with FFmpeg, use the configure option `--enable-version3` to
upgrade FFmpeg to the LGPL v3.

The VMAF, mbedTLS, RK MPI, OpenCORE and VisualOn libraries are under the Apache License
2.0. That license is incompatible with the LGPL v2.1 and the GPL v2, but not with
version 3 of those licenses. So to combine these libraries with FFmpeg, the
license version needs to be upgraded by passing `--enable-version3` to configure.

The smbclient library is under the GPL v3, to combine it with FFmpeg,
the options `--enable-gpl` and `--enable-version3` have to be passed to
configure to upgrade FFmpeg to the GPL v3.

### Incompatible libraries

There are certain libraries you can combine with FFmpeg whose licenses are not
compatible with the GPL and/or the LGPL. If you wish to enable these
libraries, even in circumstances that their license may be incompatible, pass
`--enable-nonfree` to configure. This will cause the resulting binary to be
unredistributable.

The Fraunhofer FDK AAC and OpenSSL libraries are under licenses which are
incompatible with the GPLv2 and v3. To the best of our knowledge, they are
compatible with the LGPL.
Binary file added tubic/thirdparty/ffmpeg/bin/ffmpeg.exe
Binary file not shown.
25 changes: 25 additions & 0 deletions tubic/thirdparty/ffmpeg/ffmpeg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import subprocess
import os

from tubic.utils import fix_path

location = fix_path("tubic/thirdparty/ffmpeg/bin/ffmpeg.exe")


def ffmpeg__to_mp3(
from_file: str,
bitrate: int = 192,
):
# ffmpeg -i input.wav -vn -ar 44100 -ac 2 -b:a 192k output.mp3
f_root, f_ext = os.path.splitext(from_file)
if f_ext == ".mp3":
return from_file
to_file = os.path.join(f_root, ".mp3")

ffmpeg_args = location
ffmpeg_args.extend(["-i", from_file])
ffmpeg_args.extend(["-b:a", f"{bitrate}k"])
ffmpeg_args.append(to_file)
subprocess.run(ffmpeg_args)

return to_file
11 changes: 11 additions & 0 deletions tubic/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os.path
import sys


def fix_path(path: str) -> str:
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
# _MEIPASS - the env-var pyinstaller sets when the packed application launches
# - it contains the path to the temp directory with the distribution
return os.path.join(sys._MEIPASS, path)
else:
return path
Loading

0 comments on commit bbbed49

Please sign in to comment.