Clean up everything

This commit is contained in:
Nguyễn Gia Phong 2017-06-10 22:14:15 +07:00 committed by Nguyễn Gia Phong
parent 98c73ae8ac
commit 9129a8974f
13 changed files with 614 additions and 157 deletions

View File

@ -1,2 +1 @@
# Include the license file include LICENSE README.rst doc/screenshot.png
include LICENSE README.rst

View File

@ -1,36 +1,39 @@
================================= ===============================
comp - Curses Online Media Player comp - Curses Omni Media Player
================================= ===============================
This program is a curses front-end for mpv and youtube-dl. This program is a curses front-end for mpv and youtube-dl.
.. image:: https://ipfs.io/ipfs/QmVhz4F53Sym48kXC7vhDMFsfvJ7iL8gaQ1EgoQADJvuAB .. image:: doc/screenshot.png
Installation Installation
------------ ------------
Dependencies comp requires Python 3.5+ with ``curses`` module (only available on Unix-like
^^^^^^^^^^^^ OSes such as GNU/Linux and the BSDs) and ``libmpv``. It also depends on
``python-mpv`` and ``youtube-dl`` but the setup program will automatically
install them if they are missing.
This program currently only runs on Python 3.5+ on operating systems that the Using pip
``curses`` module is supported (i.e. Unix-like OS, e.g. GNU/Linux, macOS and ^^^^^^^^^
the BSDs).
It also depends on ``youtube-dl`` and ``libmpv``. Both of those should be Python 2 is still the default on most distributions so the command would be
available in your operating system's repository, although it's more ``pip3 install comp``. You can use the ``--user`` flag to avoid system-wide
recommended to install ``youtube-dl`` using ``pip`` (currently most distros installation.
still use Python 2 as default so the command is something like ``pip3 install
youtube-dl``).
Installing comp Using setup.py
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
I will try to upload the program to PyPI when it's more completed but as of To install the latest version or test the development branch (called
this moment, I'd suggest you to use ``git`` to get the software:: ``bachelor``, in contrast to ``master``), you'll need to do it manually::
git clone https://github.com/McSinyx/comp.git git clone https://github.com/McSinyx/comp.git
cd comp cd comp
sudo ./setup.py install git checkout bachelor # usually master is synced with the PyPI repo
sudo ./setup.py install -e .
Note ``setup.py`` uses ``setuptools`` which is a third-party module and can be
install using ``pip3``.
Usage Usage
----- -----
@ -88,6 +91,8 @@ Keyboard control
+--------------+---------------------------------------------+ +--------------+---------------------------------------------+
| ``d`` | Delete current entry | | ``d`` | Delete current entry |
+--------------+---------------------------------------------+ +--------------+---------------------------------------------+
| ``i`` | Insert playlist |
+--------------+---------------------------------------------+
| ``m``, ``M`` | Cycle through playing modes | | ``m``, ``M`` | Cycle through playing modes |
+--------------+---------------------------------------------+ +--------------+---------------------------------------------+
| ``n`` | Repeat previous search | | ``n`` | Repeat previous search |
@ -120,8 +125,8 @@ Keyboard control
Configuration files Configuration files
------------------- -------------------
The system-wide configuration file is ``/etc/comp/settings.ini``, the If not specified by the ``--config``, (user-specific) configuration file is
user-specific one is ``~/.config/mpv/settings.ini``. Default configurations ``~/.config/mpv/settings.ini``. Default configurations
are listed below:: are listed below::
[comp] [comp]

87
comp
View File

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# # comp - Curses Omni Media Player
# comp - Curses Online Media Player
# #
# comp is free software: you can redistribute it and/or modify # comp is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -24,18 +23,20 @@ from argparse import ArgumentParser
from collections import deque from collections import deque
from configparser import ConfigParser from configparser import ConfigParser
from functools import reduce from functools import reduce
from gettext import gettext as _, textdomain from gettext import bindtextdomain, gettext as _, textdomain
from os import makedirs from os import makedirs
from os.path import abspath, dirname, expanduser, isfile from os.path import abspath, dirname, expanduser, isfile
from threading import Thread from threading import Thread
from youtube_dl import YoutubeDL
from mpv import MPV from mpv import MPV
from pkg_resources import resource_filename
from youtube_dl import YoutubeDL
from omp import extract_info, Omp from omp import extract_info, Omp
# Init gettext # Init gettext
textdomain('comp') bindtextdomain('omp', resource_filename('omp', 'locale'))
textdomain('omp')
# Global constants # Global constants
SYSTEM_CONFIG = '/etc/comp/settings.ini' SYSTEM_CONFIG = '/etc/comp/settings.ini'
@ -92,6 +93,8 @@ class Comp(Omp):
' ' if self.vid == 'no' else 'V') ' ' if self.vid == 'no' else 'V')
adds(right.rjust(curses.COLS), curses.color_pair(12)) adds(right.rjust(curses.COLS), curses.color_pair(12))
try: try:
self.played[self.playing]['duration'] = self.mp.osd.duration
self.print(self.played[self.playing])
left = ' {} / {} {} '.format( left = ' {} / {} {} '.format(
self.mp.osd.time_pos, self.mp.osd.duration, self.mp.osd.time_pos, self.mp.osd.duration,
'|' if self.mp.pause else '>') '|' if self.mp.pause else '>')
@ -151,7 +154,7 @@ class Comp(Omp):
def _writeln(self, y, title, duration, attr): def _writeln(self, y, title, duration, attr):
title_len = curses.COLS-DURATION_COL_LEN-3 title_len = curses.COLS-DURATION_COL_LEN-3
title = justified(title, title_len) title = justified(title, title_len)
duration = duration.ljust(DURATION_COL_LEN) duration = (duration or '00:00:00').ljust(DURATION_COL_LEN)
self.scr.addstr(y, 0, ' {} {} '.format(title, duration), attr) self.scr.addstr(y, 0, ' {} {} '.format(title, duration), attr)
self.scr.refresh() self.scr.refresh()
@ -188,7 +191,7 @@ class Comp(Omp):
def __init__(self, entries, json_file, mode, mpv_vid, mpv_vo, ytdlf): def __init__(self, entries, json_file, mode, mpv_vid, mpv_vo, ytdlf):
Omp.__init__(self, entries, lambda name, val: self.update_status(), Omp.__init__(self, entries, lambda name, val: self.update_status(),
json_file, mode, mpv_vo, mpv_vid, ytdlf) json_file, mode, mpv_vid, mpv_vo, ytdlf)
curses.noecho() curses.noecho()
curses.cbreak() curses.cbreak()
self.scr.keypad(True) self.scr.keypad(True)
@ -319,7 +322,7 @@ parser.add_argument('-e', '--extractor', default='youtube-dl',
choices=('json', 'mpv', 'youtube-dl'), required=False, choices=('json', 'mpv', 'youtube-dl'), required=False,
help=_("playlist extractor, default is youtube-dl")) help=_("playlist extractor, default is youtube-dl"))
parser.add_argument('file', help=_("path or URL to the playlist to be opened")) parser.add_argument('file', help=_("path or URL to the playlist to be opened"))
parser.add_argument('-c', '--config', required=False, parser.add_argument('-c', '--config', default=USER_CONFIG, required=False,
help=_("path to the configuration file")) help=_("path to the configuration file"))
parser.add_argument('--vid', required=False, parser.add_argument('--vid', required=False,
help=_("initial video channel. auto selects the default,\ help=_("initial video channel. auto selects the default,\
@ -333,32 +336,12 @@ parser.add_argument('-f', '--format', required=False, metavar='YTDL_FORMAT',
args = parser.parse_args() args = parser.parse_args()
entries = extract_info(args.file, args.extractor) entries = extract_info(args.file, args.extractor)
json_file = args.file if args.extractor == 'json' else '' json_file = args.file if args.extractor == 'json' else ''
config = ConfigParser() config = ConfigParser()
if args.config is not None and isfile(args.config): config.read(args.config)
config_file = args.config vid = args.vid or config.get('mpv', 'video', fallback='auto')
elif isfile(USER_CONFIG): vo = args.vo or config.get('mpv', 'video-output', fallback=None)
config_file = USER_CONFIG
else:
config_file = SYSTEM_CONFIG
config.read(config_file)
if args.vid is not None:
vid = args.vid
else:
vid = config.get('mpv', 'video', fallback='auto')
if args.vo is not None:
vo = args.vo
else:
vo = config.get('mpv', 'video-output', fallback=None)
mode = config.get('comp', 'play-mode', fallback='play-current') mode = config.get('comp', 'play-mode', fallback='play-current')
ytdlf = args.format or config.get('youtube-dl', 'format', fallback='best')
if args.format is not None:
ytdlf = args.format
else:
ytdlf = config.get('youtube-dl', 'format', fallback='best')
with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp: with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
c = comp.scr.getch() c = comp.scr.getch()
@ -399,7 +382,9 @@ with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
if s: comp.json_file = s if s: comp.json_file = s
try: try:
makedirs(dirname(abspath(comp.json_file)), exist_ok=True) makedirs(dirname(abspath(comp.json_file)), exist_ok=True)
with open(comp.json_file, 'w') as f: json.dump(comp.entries, f) with open(comp.json_file, 'w') as f:
json.dump(comp.entries, f, ensure_ascii=False,
indent=2, sort_keys=True)
except: except:
errmsg = _("'{}': Can't open file for writing").format( errmsg = _("'{}': Can't open file for writing").format(
comp.json_file) comp.json_file)
@ -407,14 +392,25 @@ with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
else: else:
comp.update_status(_("'{}' written").format(comp.json_file)) comp.update_status(_("'{}' written").format(comp.json_file))
elif c == 100: # letter d elif c == 100: # letter d
i = comp.idx() comp.entries.pop(comp.idx())
if i + 1 < len(entries): if 1 < len(comp.entries) - curses.LINES + 4 == comp.start:
comp.entries.pop(i) comp.start -= 1
elif len(entries) > 1: elif comp.idx() == len(comp.entries):
comp.entries.pop(i) comp.y -= 1
else:
comp.entries = []
comp.redraw() comp.redraw()
elif c == 105: # letter i
extractor = comp.gets(_("Playlist extractor: "))
filename = comp.gets(_("Insert: "))
entries = extract_info(filename, extractor)
if entries is None:
comp.update_status(
_("'{}': Can't extract playlist").format(filename))
else:
bottom = comp.entries[comp.idx():]
comp.entries = comp.entries[:comp.idx()]
comp.entries.extend(entries)
comp.entries.extend(bottom)
comp.redraw()
elif c == 109: # letter m elif c == 109: # letter m
comp.mode = MODES[(MODES.index(comp.mode) + 1) % 8] comp.mode = MODES[(MODES.index(comp.mode) + 1) % 8]
comp.update_status() comp.update_status()
@ -422,9 +418,14 @@ with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
comp.next_search() comp.next_search()
elif c == 111: # letter o elif c == 111: # letter o
extractor = comp.gets(_("Playlist extractor: ")) extractor = comp.gets(_("Playlist extractor: "))
comp.entries = extract_info(comp.gets(_("Open: ")), extractor) filename = comp.gets(_("Open: "))
comp.start, comp.y = 0, 1 entries = extract_info(filename, extractor)
comp.redraw() if entries is None:
comp.update_status(
_("'{}': Can't extract playlist").format(filename))
else:
comp.entries, comp.start, comp.y = entries, 0, 1
comp.redraw()
elif c == 112: # letter p elif c == 112: # letter p
comp.mp.pause ^= True comp.mp.pause ^= True
elif c in (curses.KEY_UP, 107): # up arrow or letter k elif c in (curses.KEY_UP, 107): # up arrow or letter k

BIN
doc/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 KiB

24
omp/__init__.py Normal file
View File

@ -0,0 +1,24 @@
# omp - Omni Media Player
# This is a part of comp
#
# comp is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# comp program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with comp. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2017 Nguyễn Gia Phong <vn.mcsinyx@gmail.com>
"""Omni Media Player - an handy mpv front-end library for interactive
control.
"""
from .ie import extract_info
from .omp import Omp

114
omp/ie.py Normal file
View File

@ -0,0 +1,114 @@
# ie.py - Omni Media Player infomation extractor
# This is a part of comp
#
# comp is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# comp program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with comp. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2017 Nguyễn Gia Phong <vn.mcsinyx@gmail.com>
import json
from os.path import abspath, expanduser, expandvars, isfile
from time import gmtime, sleep, strftime
from youtube_dl import YoutubeDL
from mpv import MPV
DEFAULT_ENTRY = {'filename': '', 'title': '', 'duration': '00:00:00',
'error': False, 'playing': False, 'selected': False}
YTDL_OPTS = {'quiet': True, 'default_search': 'ytsearch',
'extract_flat': 'in_playlist'}
def json_extract_info(filename):
"""Return list of entries extracted from a file using json. If an
error occur during the extraction, return None.
"""
try:
with open(filename) as f: raw_info, info = json.load(f), []
for i in raw_info:
e = DEFAULT_ENTRY.copy()
for k in e:
if k in i and isinstance(i[k], type(e[k])): e[k] = i[k]
info.append(e)
except:
return None
else:
return info
def mpv_extract_info(filename):
"""Return list of entries extracted from a path or URL using mpv. If
an error occur during the extraction, return None.
"""
mp = MPV(ytdl=True, vid=False)
mp.play(filename)
while mp.duration is None:
sleep(0.25)
if mp.playback_abort: return None
info = {'filename': filename, 'title': mp.media_title.decode(),
'duration': mp.osd.duration, 'error': False, 'playing': False,
'selected': False}
mp.quit()
return [info]
def ytdl_extract_info(filename):
"""Return list of entries extracted from a path or URL using
youtube-dl. If an error occur during the extraction, return None.
"""
with YoutubeDL(YTDL_OPTS) as ytdl:
try:
raw_info = ytdl.extract_info(filename, download=False)
except:
return None
info = raw_info.get('entries', [raw_info])
for i in info:
if 'webpage_url' in i:
i['filename'] = i['webpage_url']
elif (i['ie_key'] == 'Youtube'
or i['extractor'] == 'youtube'):
i['filename'] = 'https://youtu.be/' + i['id']
else:
i['filename'] = i['url']
if 'title' not in i:
try:
i['title'] = ytdl.extract_info(i['filename'],
download=False)['title']
except:
return None
if 'duration' not in i:
i['duration'] = '00:00:00'
elif isinstance(i['duration'], int):
i['duration'] = strftime('%H:%M:%S', gmtime(i['duration']))
for k in 'error', 'playing', 'selected': i.setdefault(k, False)
for k in i.copy():
if k not in DEFAULT_ENTRY: i.pop(k)
return info
def extract_info(filename, extractor='youtube-dl'):
"""Return list of entries extracted from a path or URL using
specified extractor. If an error occur during the extraction,
return None.
The extractor could be either 'json', 'mpv' or 'youtube-dl' and
fallback to 'youtube-dl'.
"""
if isfile(expanduser(expandvars(filename))):
filename = abspath(expanduser(expandvars(filename)))
if extractor == 'json':
return json_extract_info(filename)
elif extractor == 'mpv':
return mpv_extract_info(filename)
else:
return ytdl_extract_info(filename)

Binary file not shown.

View File

@ -0,0 +1,72 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2017-04-05 11:00+0700\n"
"PO-Revision-Date: 2017-04-06 22:29+0700\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 1.8.11\n"
"Last-Translator: \n"
"Plural-Forms: nplurals=1; plural=0;\n"
"Language: vi_VN\n"
msgid "Curses Online Media Player"
msgstr "Phần mềm chơi đa phương tiện trực tuyến sử dụng curses"
msgid "play-current"
msgstr "chơi-một"
msgid "play-all"
msgstr "chơi-tất-cả"
msgid "play-selected"
msgstr "chơi-đã-chọn"
msgid "repeat-current"
msgstr "lặp-một"
msgid "repeat-all"
msgstr "lặp-tất-cả"
msgid "repeat-selected"
msgstr "lặp-đã-chọn"
msgid "shuffle-all"
msgstr "ngẫu-nhiên-tất-cả"
msgid "shuffle-selected"
msgstr "ngẫu-nhiên-đã-chọn"
msgid "URL"
msgstr "URL"
msgid "Title"
msgstr "Tiêu đề"
msgid "Source"
msgstr "Nguồn"
msgid "Current size: {}x{}. Minimum size: {}x4."
msgstr "Kích thước hiện tại: {}x{}. Kích thước tối thiểu: {}x4."
msgid "Save playlist to [{}]:"
msgstr "Lưu playlist tại [{}]:"
msgid "'{}': Can't open file for writing"
msgstr "'{}': Không mở được tệp để ghi"
msgid "'{}' written"
msgstr "'{}' đã ghi"
msgid "path to playlist in JSON format"
msgstr "đường dẫn đến playlist ở định dạng JSON"
msgid "URL to an playlist on Youtube"
msgstr "URL của playlist trên Youtube"

View File

@ -1,4 +1,4 @@
# omp.py - comp library for playing and playlist management # omp.py - Omni Media Player meta object
# This is a part of comp # This is a part of comp
# #
# comp is free software: you can redistribute it and/or modify # comp is free software: you can redistribute it and/or modify
@ -10,7 +10,6 @@
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with comp. If not, see <http://www.gnu.org/licenses/>. # along with comp. If not, see <http://www.gnu.org/licenses/>.
@ -22,8 +21,8 @@ from collections import deque
from itertools import cycle from itertools import cycle
from os.path import abspath, expanduser, expandvars, isfile from os.path import abspath, expanduser, expandvars, isfile
from random import choice from random import choice
from requests import head
from time import gmtime, sleep, strftime from time import gmtime, sleep, strftime
from urllib import request
from youtube_dl import YoutubeDL from youtube_dl import YoutubeDL
from mpv import MPV, MpvFormat from mpv import MPV, MpvFormat
@ -34,79 +33,8 @@ YTDL_OPTS = {'quiet': True, 'default_search': 'ytsearch',
'extract_flat': 'in_playlist'} 'extract_flat': 'in_playlist'}
def extract_info(filename, extractor='youtube-dl'):
"""Return list of entries extracted from a path or URL using
specified extractor.
The extractor could be either 'json', 'mpv' or 'youtube-dl'. If it
is not one of them or not specified, youtube-dl will be used.
"""
def json_extract_info(filename):
try:
with open(filename) as f: raw_info = json.load(f)
info = []
for i in raw_info:
e = DEFAULT_ENTRY.copy()
for k in e:
if k in i and isinstance(i[k], type(e[k])): e[k] = i[k]
info.append(e)
except:
return []
else:
return info
def mpv_extract_info(filename):
mp = MPV(ytdl=True)
mp.play(filename)
while mp.duration is None:
sleep(0.25)
if mp.playback_abort: return []
info = {'filename': filename, 'title': mp.media_title.decode(),
'duration': mp.osd.duration, 'error': False, 'playing': False,
'selected': False}
mp.quit()
return [info]
def ytdl_extract_info(filename):
with YoutubeDL(YTDL_OPTS) as ytdl:
raw_info = ytdl.extract_info(filename, download=False)
info = raw_info.get('entries', [raw_info])
for i in info:
if 'webpage_url' in i:
i['filename'] = i['webpage_url']
elif (i['ie_key'] == 'Youtube'
or i['extractor'] == 'youtube'):
i['filename'] = 'https://youtu.be/' + i['id']
else:
i['filename'] = i['url']
if 'title' not in i:
i['title'] = ytdl.extract_info(i['filename'],
download=False)['title']
if 'duration' not in i:
i['duration'] = '00:00:00'
elif isinstance(i['duration'], int):
i['duration'] = strftime('%H:%M:%S', gmtime(i['duration']))
for k in 'error', 'playing', 'selected': i.setdefault(k, False)
for k in i.copy():
if k not in DEFAULT_ENTRY: i.pop(k)
return info
try:
if (extractor != 'youtube-dl' and head(filename).status_code >= 400
and isfile(expanduser(expandvars(filename)))):
filename = abspath(expanduser(expandvars(filename)))
except:
pass
if extractor == 'json':
return json_extract_info(filename)
elif extractor == 'mpv':
return mpv_extract_info(filename)
else:
return ytdl_extract_info(filename)
class Omp(object): class Omp(object):
"""Meta object for playing and playlist management. """Omni Media Player meta object.
Attributes: Attributes:
entries (list): list of all tracks entries (list): list of all tracks
@ -174,7 +102,7 @@ class Omp(object):
pass pass
def next(self, force=False, backward=False): def next(self, force=False, backward=False):
comp.play_backward = backward self.play_backward = backward
if self.mp.idle_active: if self.mp.idle_active:
self.play(force) self.play(force)
else: else:

View File

@ -1,8 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from os import walk from os import listdir
from os.path import join from os.path import join
from sys import prefix
from setuptools import setup from setuptools import setup
@ -11,8 +10,8 @@ with open('README.rst') as f:
setup( setup(
name='comp', name='comp',
version='0.3.1', version='0.3.2',
description=('Curses Online Media Player'), description=('Curses Omni Media Player'),
long_description=long_description, long_description=long_description,
url='https://github.com/McSinyx/comp', url='https://github.com/McSinyx/comp',
author='Nguyễn Gia Phong', author='Nguyễn Gia Phong',
@ -29,16 +28,10 @@ setup(
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
'Topic :: Multimedia :: Sound/Audio :: Players', 'Topic :: Multimedia :: Sound/Audio :: Players',
'Topic :: Multimedia :: Video :: Display' 'Topic :: Multimedia :: Video :: Display'],
],
keywords='youtube-dl mpv-wrapper curses console-application multimedia', keywords='youtube-dl mpv-wrapper curses console-application multimedia',
packages=['omp'],
install_requires=['python-mpv', 'youtube-dl'], install_requires=['python-mpv', 'youtube-dl'],
data_files=[ package_data={'omp': ['locale/*/LC_MESSAGES/omp.mo']},
*((join(prefix, 'share', i[0]), [join(i[0], 'comp.mo')])
for i in walk('locale') if i[2]),
('/etc/comp', ['settings.ini'])
],
py_modules=['omp'],
scripts=['comp'], scripts=['comp'],
platforms=['POSIX'] platforms=['POSIX'])
)

File diff suppressed because one or more lines are too long

BIN
test/gplv3.ogg Normal file

Binary file not shown.

322
test/playlist.json Normal file
View File

@ -0,0 +1,322 @@
[
{
"duration": "00:05:21",
"error": false,
"filename": "https://youtu.be/weeI1G46q0o",
"playing": false,
"selected": true,
"title": "DJ Khaled - I'm the One ft. Justin Bieber, Quavo, Chance the Rapper, Lil Wayne"
},
{
"duration": "00:04:23",
"error": false,
"filename": "https://youtu.be/JGwWNGJdvx8",
"playing": false,
"selected": true,
"title": "Ed Sheeran - Shape of You [Official Video]"
},
{
"duration": "00:03:30",
"error": false,
"filename": "https://youtu.be/PMivT7MJ41M",
"playing": false,
"selected": true,
"title": "Bruno Mars - Thats What I Like [Official Video]"
},
{
"duration": "00:04:46",
"error": false,
"filename": "https://youtu.be/CTFtOOh47oo",
"playing": false,
"selected": false,
"title": "French Montana - Unforgettable ft. Swae Lee"
},
{
"duration": "00:04:45",
"error": false,
"filename": "https://youtu.be/NLZRYQMLDW4",
"playing": false,
"selected": false,
"title": "Kendrick Lamar - DNA."
},
{
"duration": "00:04:07",
"error": false,
"filename": "https://youtu.be/FM7MFYoylVs",
"playing": false,
"selected": true,
"title": "The Chainsmokers & Coldplay - Something Just Like This (Lyric)"
},
{
"duration": "00:03:48",
"error": false,
"filename": "https://youtu.be/72UO0v5ESUo",
"playing": false,
"selected": true,
"title": "Luis Fonsi, Daddy Yankee - Despacito (Audio) ft. Justin Bieber"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/D5drYkLiLI8",
"playing": false,
"selected": false,
"title": "Kygo, Selena Gomez - It Ain't Me (with Selena Gomez) (Audio)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/Zgmvg-zzctI",
"playing": false,
"selected": false,
"title": "Lil Uzi Vert - XO TOUR Llif3 (Produced By TM88)"
},
{
"duration": "00:12:53",
"error": false,
"filename": "test/gplv3.ogg",
"playing": false,
"selected": true,
"title": "gplv3.ogg"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/xvZqHgFz51I",
"playing": false,
"selected": false,
"title": "Future - Mask Off"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/8j9zMok6two",
"playing": false,
"selected": false,
"title": "Miley Cyrus - Malibu (Official Video)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/dPI-mRFEIH0",
"playing": false,
"selected": false,
"title": "Katy Perry - Bon Appétit (Official) ft. Migos"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/aatr_2MstrI",
"playing": false,
"selected": false,
"title": "Clean Bandit - Symphony feat. Zara Larsson [Official Video]"
},
{
"duration": "00:34:38",
"error": false,
"filename": "https://www.tube8.com/teen/nicole-ray-and-james-deen/409802/",
"playing": false,
"selected": true,
"title": "Nicole Ray and James Deen"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/7F37r50VUTQ",
"playing": false,
"selected": true,
"title": "ZAYN, Taylor Swift - I Dont Wanna Live Forever (Fifty Shades Darker)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/qFLhGq0060w",
"playing": false,
"selected": true,
"title": "The Weeknd - I Feel It Coming ft. Daft Punk"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/6ImFf__U6io",
"playing": false,
"selected": true,
"title": "Birdman - Dark Shades (Explicit) ft. Lil Wayne, Mack Maine"
},
{
"duration": "00:03:56",
"error": false,
"filename": "https://www.youtube.com/watch?v=3M3xfu0m5o4",
"playing": false,
"selected": false,
"title": "David Banner - Play (Dirty version)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/NGLxoKOvzu4",
"playing": false,
"selected": false,
"title": "Jason Derulo - Swalla (feat. Nicki Minaj & Ty Dolla $ign) (Official Music Video)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/Hm1YFszJWbQ",
"playing": false,
"selected": false,
"title": "Migos - Slippery feat. Gucci Mane [Official Video]"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/SC4xMk98Pdc",
"playing": false,
"selected": false,
"title": "Post Malone - Congratulations ft. Quavo"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/nfs8NYg7yQM",
"playing": false,
"selected": false,
"title": "Charlie Puth - Attention [Official Video]"
},
{
"duration": "00:04:10",
"error": false,
"filename": "https://www.youtube.com/watch?v=sRIkXM8S1J8",
"playing": false,
"selected": true,
"title": "Best Goat Song Versions Compilation Ever! (HD)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/Dst9gZkq1a8",
"playing": false,
"selected": false,
"title": "Travis Scott - goosebumps ft. Kendrick Lamar"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/dMK_npDG12Q",
"playing": false,
"selected": false,
"title": "Lorde - Green Light"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/h--P8HzYZ74",
"playing": false,
"selected": true,
"title": "Zedd, Alessia Cara - Stay (Lyric Video)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/Mdh2p03cRfw",
"playing": false,
"selected": false,
"title": "Sam Hunt - Body Like A Back Road (Audio)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/Fq0xEpRDL9Q",
"playing": false,
"selected": false,
"title": "Chris Brown - Privacy (Explicit Version)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/7wtfhZwyrcc",
"playing": false,
"selected": false,
"title": "Imagine Dragons - Believer"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/t_jHrUE5IOk",
"playing": false,
"selected": false,
"title": "Maluma - Felices los 4 (Official Video)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/wzZWXrlDj-A",
"playing": false,
"selected": false,
"title": "DNCE - Kissing Strangers ft. Nicki Minaj"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/AEB6ibtdPZc",
"playing": false,
"selected": false,
"title": "Paramore: Hard Times [OFFICIAL VIDEO]"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/vqW18C4plZ8",
"playing": false,
"selected": false,
"title": "WizKid - Come Closer ft. Drake"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/A7xzXDStQnk",
"playing": false,
"selected": false,
"title": "Shawn Mendes - There's Nothing Holdin' Me Back (Lyric Video)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/FG9M0aEpJGE",
"playing": false,
"selected": false,
"title": "G-Eazy & Kehlani - Good Life (from The Fate of the Furious: The Album) [MUSIC VIDEO]"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/vp8VZe5kqEM",
"playing": false,
"selected": false,
"title": "Lady Gaga - The Cure (Audio)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/eP4eqhWc7sI",
"playing": false,
"selected": false,
"title": "Lana Del Rey - Lust For Life (Official Video) ft. The Weeknd"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/5qJp6xlKEug",
"playing": false,
"selected": false,
"title": "Gorillaz - Saturnz Barz (Spirit House)"
},
{
"duration": "00:00:00",
"error": false,
"filename": "https://youtu.be/9sg-A-eS6Ig",
"playing": false,
"selected": true,
"title": "Enrique Iglesias - SUBEME LA RADIO (Official Video) ft. Descemer Bueno, Zion & Lennox"
}
]