<gh_stars>1-10
#! /usr/bin/env python
## Hey, Python: encoding=utf-8
#
# Copyright (c) 2007-2008, 2010 <NAME> (<EMAIL>)
# Licensed under the terms of the MIT license.

import justrok

import os
import random
import stat
import time

from PyQt5.QtCore import pyqtSignal, QObject, QTimer
from PyQt5.QtWidgets import QAction

def fmt_seconds(seconds):
    """Convert a number of seconds to m:ss or h:mm:ss notation."""
    try:
        seconds = int(seconds)
    except ValueError:
        justrok.logger.warn('invalid int passed to fmt_seconds(): %r', seconds)
        return seconds

    if seconds < 3600:
        return '%d:%02d' % (seconds//60, seconds%60)
    else:
        return '%d:%02d:%02d' % (seconds//3600, (seconds%3600)//60, seconds%60)

def create_action(name, text, slot, icon=None, shortcut=None):
    action = QAction(None)
    action.setObjectName(name)
    action.setText(text)
    action.triggered.connect(slot)
    justrok.Globals.actions.add(action)
    if icon is not None:
        action.setIcon(icon)
    if shortcut is not None:
        action.setShortcut(shortcut)
    return action


def playable_from_untrusted(files):
    """Filter a list of untrusted paths to only include playable files.

    This method takes a list of paths, and drops from it files that do not
    exist or the engine can't play. Directories will be read and all its files
    included as appropriate.

    Args:
      files: list of paths.
      warn: if true, emit a warning for each skipped file, stating the reason;
        if false, debug() statements will be emitted instead.
    """
    result = []
    def append_path(path):
        try:
            mode = os.stat(path).st_mode
        except OSError as e:
            justrok.logger.warn('skipping %r: %s', path, e.strerror)
            return

        if stat.S_ISDIR(mode):
            try:
                contents = sorted(os.listdir(path))
            except OSError as e:
                justrok.logger.warn('skipping %r: %s', path, e.strerror)
            else:
                for entry in contents:
                    append_path(os.path.join(path, entry))
        elif stat.S_ISREG(mode):
            if os.path.splitext(path)[1] == ".m3u":
                head, tail = os.path.split(path)
                for line in open(path):
                    if line.startswith('#') == True:
                        justrok.logger.warn("Skipping comment line \"%r\".")
                    else:
                        append_path(os.path.join(head, line.strip()))
            elif justrok.Globals.engine.can_play_path(path):
                if path not in result:
                    result.append(path)
            else:
                justrok.logger.warn('skipping %r: not a playable format', path)
        else:
            justrok.logger.warn('skipping %r: not a regular file', path)

    for f in files:
        append_path(f)

    return result


def contiguous_chunks(intlist):
    """Calculate a list of contiguous areas in a possibly unsorted list.

    >>> contiguous_chunks([2, 9, 3, 5, 8, 1])
    [(1, 3), (5, 1), (8, 2)]
    """
    if len(intlist) == 0:
        return []

    mylist = sorted(intlist)
    result = [[mylist[0], 1]]

    for x in mylist[1:]:
        if x == sum(result[-1]):
            result[-1][1] += 1
        else:
            result.append([x, 1])

    return list(map(tuple, result))


def creat_excl(path, mode=0o644):
    """Return a write-only file object created with O_EXCL."""
    fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, mode)
    return os.fdopen(fd, 'w')

##

class CallbackRegistry(object):
    def __init__(self):
        self.__at_exit = list()
        self.__apply_settings = list()
    
    @staticmethod
    def __register(callbacks, callback):
        callbacks.append(callback)
    
    @staticmethod
    def __invoke_callbacks(callbacks):
        for callback in callbacks:
            callback()
    
    def register_at_exit(self, callback):
        self.__register(self.__at_exit, callback)
    
    def register_apply_settings(self, callback):
        self.__register(self.__apply_settings, callback)
    
    def fire_at_exit(self):
        self.__invoke_callbacks(self.__at_exit)
    
    def fire_apply_settings(self):
        self.__invoke_callbacks(self.__apply_settings)

##

class QTimerWithPause(QObject):
    """A QTimer with pause() and resume() methods.

    Note that we don't inherit from QTimer, and we just offer a limited
    interface: start(msecs), stop(), pause(), resume(), and setSingleShot().
    The timeout() signal is emitted as normal.
    """
    
    timeout = pyqtSignal()
    
    def __init__(self, *args):
        QObject.__init__(self, *args)
        self._timer = QTimer(self)
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._on_timer_timeout)
        self._running = False
        self._duration = None  # Duration as given to start().
        self._remaining = None  # What's pending, considering pauses().
        self._start_time = None  # Time we last _started().
        self._single_shot = False
    
    def start(self, msecs):
        self._duration = self._remaining = msecs
        self._start()
    
    def stop(self):
        self._remaining = self._duration
        self._timer.stop()
    
    def pause(self):
        if self._timer.isActive():
            self._timer.stop()
            elapsed = time.time() - self._start_time
            self._remaining -= int(elapsed*1000)
    
    def resume(self):
        if (self._running
                and not self._timer.isActive()):
            self._start()
    
    def setSingleShot(self, value):
        self._single_shot = bool(value)
    
    def _start(self):
        self._running = True
        self._start_time = time.time()
        self._timer.start(self._remaining)
    
    def _on_timer_timeout(self):
        self._running = False
        self._remaining = self._duration
        self.timeout.emit()
        if not self._single_shot:
            self._start()

class RandomOrderedList(list):
    """A list where append() inserts items at a random position."""

    def append(self, item):
        self.insert(random.randrange(len(self)+1), item)

    def extend(self, seq):
        list.extend(self, seq)
        random.shuffle(self)

def get_first(sequence, predicate):
    for item in sequence:
        if predicate(item) is True:
            return item
    return None

def get_n_tracks_string(amount):
    if amount == 1:
        return '1 track'
    else:
        return '%d tracks' % (amount,)

#! /usr/bin/env python
## Hey, Python: encoding=utf-8
#
# Copyright (c) 2007-2008, 2010 <NAME> (<EMAIL>)
# Licensed under the terms of the MIT license.

import justrok

import os
import random
import stat
import time

from PyQt5.QtCore import pyqtSignal, QObject, QTimer
from PyQt5.QtWidgets import QAction

def fmt_seconds(seconds):
    """Convert a number of seconds to m:ss or h:mm:ss notation."""
    try:
        seconds = int(seconds)
    except ValueError:
        justrok.logger.warn('invalid int passed to fmt_seconds(): %r', seconds)
        return seconds

    if seconds < 3600:
        return '%d:%02d' % (seconds//60, seconds%60)
    else:
        return '%d:%02d:%02d' % (seconds//3600, (seconds%3600)//60, seconds%60)

def create_action(name, text, slot, icon=None, shortcut=None):
    action = QAction(None)
    action.setObjectName(name)
    action.setText(text)
    action.triggered.connect(slot)
    justrok.Globals.actions.add(action)
    if icon is not None:
        action.setIcon(icon)
    if shortcut is not None:
        action.setShortcut(shortcut)
    return action


def playable_from_untrusted(files):
    """Filter a list of untrusted paths to only include playable files.

    This method takes a list of paths, and drops from it files that do not
    exist or the engine can't play. Directories will be read and all its files
    included as appropriate.

    Args:
      files: list of paths.
      warn: if true, emit a warning for each skipped file, stating the reason;
        if false, debug() statements will be emitted instead.
    """
    result = []
    def append_path(path):
        try:
            mode = os.stat(path).st_mode
        except OSError as e:
            justrok.logger.warn('skipping %r: %s', path, e.strerror)
            return

        if stat.S_ISDIR(mode):
            try:
                contents = sorted(os.listdir(path))
            except OSError as e:
                justrok.logger.warn('skipping %r: %s', path, e.strerror)
            else:
                for entry in contents:
                    append_path(os.path.join(path, entry))
        elif stat.S_ISREG(mode):
            if os.path.splitext(path)[1] == ".m3u":
                head, tail = os.path.split(path)
                for line in open(path):
                    if line.startswith('#') == True:
                        justrok.logger.warn("Skipping comment line \"%r\".")
                    else:
                        append_path(os.path.join(head, line.strip()))
            elif justrok.Globals.engine.can_play_path(path):
                if path not in result:
                    result.append(path)
            else:
                justrok.logger.warn('skipping %r: not a playable format', path)
        else:
            justrok.logger.warn('skipping %r: not a regular file', path)

    for f in files:
        append_path(f)

    return result


def contiguous_chunks(intlist):
    """Calculate a list of contiguous areas in a possibly unsorted list.

    >>> contiguous_chunks([2, 9, 3, 5, 8, 1])
    [(1, 3), (5, 1), (8, 2)]
    """
    if len(intlist) == 0:
        return []

    mylist = sorted(intlist)
    result = [[mylist[0], 1]]

    for x in mylist[1:]:
        if x == sum(result[-1]):
            result[-1][1] += 1
        else:
            result.append([x, 1])

    return list(map(tuple, result))


def creat_excl(path, mode=0o644):
    """Return a write-only file object created with O_EXCL."""
    fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, mode)
    return os.fdopen(fd, 'w')

##

class CallbackRegistry(object):
    def __init__(self):
        self.__at_exit = list()
        self.__apply_settings = list()
    
    @staticmethod
    def __register(callbacks, callback):
        callbacks.append(callback)
    
    @staticmethod
    def __invoke_callbacks(callbacks):
        for callback in callbacks:
            callback()
    
    def register_at_exit(self, callback):
        self.__register(self.__at_exit, callback)
    
    def register_apply_settings(self, callback):
        self.__register(self.__apply_settings, callback)
    
    def fire_at_exit(self):
        self.__invoke_callbacks(self.__at_exit)
    
    def fire_apply_settings(self):
        self.__invoke_callbacks(self.__apply_settings)

##

class QTimerWithPause(QObject):
    """A QTimer with pause() and resume() methods.

    Note that we don't inherit from QTimer, and we just offer a limited
    interface: start(msecs), stop(), pause(), resume(), and setSingleShot().
    The timeout() signal is emitted as normal.
    """
    
    timeout = pyqtSignal()
    
    def __init__(self, *args):
        QObject.__init__(self, *args)
        self._timer = QTimer(self)
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._on_timer_timeout)
        self._running = False
        self._duration = None  # Duration as given to start().
        self._remaining = None  # What's pending, considering pauses().
        self._start_time = None  # Time we last _started().
        self._single_shot = False
    
    def start(self, msecs):
        self._duration = self._remaining = msecs
        self._start()
    
    def stop(self):
        self._remaining = self._duration
        self._timer.stop()
    
    def pause(self):
        if self._timer.isActive():
            self._timer.stop()
            elapsed = time.time() - self._start_time
            self._remaining -= int(elapsed*1000)
    
    def resume(self):
        if (self._running
                and not self._timer.isActive()):
            self._start()
    
    def setSingleShot(self, value):
        self._single_shot = bool(value)
    
    def _start(self):
        self._running = True
        self._start_time = time.time()
        self._timer.start(self._remaining)
    
    def _on_timer_timeout(self):
        self._running = False
        self._remaining = self._duration
        self.timeout.emit()
        if not self._single_shot:
            self._start()

class RandomOrderedList(list):
    """A list where append() inserts items at a random position."""

    def append(self, item):
        self.insert(random.randrange(len(self)+1), item)

    def extend(self, seq):
        list.extend(self, seq)
        random.shuffle(self)

def get_first(sequence, predicate):
    for item in sequence:
        if predicate(item) is True:
            return item
    return None

def get_n_tracks_string(amount):
    if amount == 1:
        return '1 track'
    else:
        return '%d tracks' % (amount,)
#! /usr/bin/env python ## Hey, Python: encoding=utf-8 # # Copyright (c) 2007-2008, 2010 <NAME> (<EMAIL>) # Licensed under the terms of the MIT license. Convert a number of seconds to m:ss or h:mm:ss notation. Filter a list of untrusted paths to only include playable files.      This method takes a list of paths, and drops from it files that do not     exist or the engine can't play. Directories will be read and all its files     included as appropriate.      Args:       files: list of paths.       warn: if true, emit a warning for each skipped file, stating the reason;         if false, debug() statements will be emitted instead. Calculate a list of contiguous areas in a possibly unsorted list.      >>> contiguous_chunks([2, 9, 3, 5, 8, 1])     [(1, 3), (5, 1), (8, 2)] Return a write-only file object created with O_EXCL. ## ## A QTimer with pause() and resume() methods.      Note that we don't inherit from QTimer, and we just offer a limited     interface: start(msecs), stop(), pause(), resume(), and setSingleShot().     The timeout() signal is emitted as normal. # Duration as given to start(). # What's pending, considering pauses(). # Time we last _started(). A list where append() inserts items at a random position.