|
17 | 17 | from os.path import join, basename, getmtime, getsize
|
18 | 18 | from platform import system
|
19 | 19 | from time import ctime, sleep
|
20 |
| -#from copy import deepcopy |
21 |
| -#from textwrap import wrap |
| 20 | +from datetime import datetime |
22 | 21 |
|
23 | 22 | from .common import *
|
24 | 23 | from .config_window import *
|
@@ -98,10 +97,14 @@ class PyRadio(object):
|
98 | 97 |
|
99 | 98 | _old_config_encoding = ''
|
100 | 99 |
|
101 |
| - |
102 |
| - _update_version = '0.8' |
103 |
| - _update_notify_lock = threading.Lock() |
| 100 | + # update notification |
| 101 | + _update_version = '' |
104 | 102 | _update_version_do_display = ''
|
| 103 | + _update_notification_thread = None |
| 104 | + stop_update_notification_thread = False |
| 105 | + _update_notify_lock = threading.Lock() |
| 106 | + |
| 107 | + _locked = False |
105 | 108 |
|
106 | 109 | def __init__(self, pyradio_config, play=False, req_player='', theme=''):
|
107 | 110 | self._cnf = pyradio_config
|
@@ -413,7 +416,14 @@ def run(self):
|
413 | 416 | else:
|
414 | 417 | # start update thread
|
415 | 418 | if CAN_CHECK_FOR_UPDATES:
|
416 |
| - pass |
| 419 | + if self._cnf.locked: |
| 420 | + if logger.isEnabledFor(logging.INFO): |
| 421 | + logger.info('(detectUpdateThread): session locked. Not starting!!!') |
| 422 | + else: |
| 423 | + self._update_notification_thread = threading.Thread(target=self.detectUpdateThread, |
| 424 | + args=(self._cnf.stations_dir, self._update_notify_lock, |
| 425 | + lambda: self.stop_update_notification_thread)) |
| 426 | + self._update_notification_thread.start() |
417 | 427 |
|
418 | 428 | #signal.signal(signal.SIGINT, self.ctrl_c_handler)
|
419 | 429 | self.log.write('Selected player: {}'.format(self._format_player_string()), help_msg=True)
|
@@ -458,6 +468,24 @@ def ctrl_c_handler(self, signum, frame):
|
458 | 468 | """ Try to auto save config on exit
|
459 | 469 | Do not check result!!! """
|
460 | 470 | self._cnf.save_config()
|
| 471 | + if not self._cnf.locked: |
| 472 | + lock_file = path.join(self._cnf.stations_dir, '.lock') |
| 473 | + if path.exists(lock_file): |
| 474 | + try: |
| 475 | + os.remove(lock_file) |
| 476 | + if logger.isEnabledFor(logging.INFO): |
| 477 | + logger.info('Lock file removed...') |
| 478 | + except: |
| 479 | + if logger.isEnabledFor(logging.INFO): |
| 480 | + logger.info('Failed to remove Lock file...') |
| 481 | + else: |
| 482 | + if logger.isEnabledFor(logging.INFO): |
| 483 | + logger.info('Lock file not found...') |
| 484 | + |
| 485 | + if self._update_notification_thread: |
| 486 | + if self._update_notification_thread.is_alive(): |
| 487 | + self.stop_update_notification_thread = True |
| 488 | + self._update_notification_thread.join() |
461 | 489 |
|
462 | 490 | def _goto_playing_station(self, changing_playlist=False):
|
463 | 491 | """ make sure playing station is visible """
|
@@ -1510,6 +1538,169 @@ def _show_config_window(self):
|
1510 | 1538 | self._config_win.parent = self.bodyWin
|
1511 | 1539 | self._config_win.refresh_config_win()
|
1512 | 1540 |
|
| 1541 | + |
| 1542 | + def detectUpdateThread(self, a_path, a_lock, stop): |
| 1543 | + """ a thread to check if an update is available """ |
| 1544 | + |
| 1545 | + def delay(secs, stop): |
| 1546 | + for i in range(0, 2 * secs): |
| 1547 | + sleep(.5) |
| 1548 | + if stop(): |
| 1549 | + return |
| 1550 | + |
| 1551 | + def to_ver(this_version): |
| 1552 | + a_v = this_version.replace('-r', '.') |
| 1553 | + a_l = a_v.split('.') |
| 1554 | + a_n_l = [] |
| 1555 | + for n in a_l: |
| 1556 | + a_n_l.append(int(n)) |
| 1557 | + return a_n_l |
| 1558 | + |
| 1559 | + def clean_date_files(files, start=0): |
| 1560 | + files_to_delete = files[start+1:] |
| 1561 | + for a_file in files_to_delete: |
| 1562 | + try: |
| 1563 | + remove(a_file) |
| 1564 | + except: |
| 1565 | + pass |
| 1566 | + |
| 1567 | + def create_tadays_date_file(a_path): |
| 1568 | + d1 = datetime.now() |
| 1569 | + now_str = d1.strftime('%Y-%m-%d') |
| 1570 | + try: |
| 1571 | + with open(path.join(a_path, '.' + now_str + '.date'), 'w') as f: |
| 1572 | + pass |
| 1573 | + except: |
| 1574 | + pass |
| 1575 | + |
| 1576 | + if logger.isEnabledFor(logging.DEBUG): |
| 1577 | + logger.debug('detectUpdateThread: starting...') |
| 1578 | + ################## |
| 1579 | + #delay(5, stop) |
| 1580 | + from pyradio import version as this_version |
| 1581 | + connection_fail_count = 0 |
| 1582 | + delay(random.randint(25, 45), stop) |
| 1583 | + if stop(): |
| 1584 | + if logger.isEnabledFor(logging.DEBUG): |
| 1585 | + logger.debug('detectUpdateThread: Asked to stop. Stoping...') |
| 1586 | + return |
| 1587 | + files = glob.glob(path.join(a_path, '.*.date')) |
| 1588 | + if files: |
| 1589 | + files.sort(reverse=True) |
| 1590 | + if len(files) > 1: |
| 1591 | + clean_date_files(files) |
| 1592 | + a_date = path.split(path.splitext(files[0])[0])[1][1:] |
| 1593 | + |
| 1594 | + |
| 1595 | + d1 = datetime.now() |
| 1596 | + d2 = datetime.strptime(a_date, '%Y-%m-%d') |
| 1597 | + delta = (d1 - d2).days |
| 1598 | + |
| 1599 | + if delta > 3: |
| 1600 | + if logger.isEnabledFor(logging.DEBUG): |
| 1601 | + logger.debug('detectUpdateThread: checking for updates') |
| 1602 | + else: |
| 1603 | + if logger.isEnabledFor(logging.DEBUG): |
| 1604 | + if 3 - delta == 1: |
| 1605 | + logger.debug('detectUpdateThread: Will check tomorrow...') |
| 1606 | + else: |
| 1607 | + logger.debug('detectUpdateThread: Will check in {} days...'.format(3 - delta)) |
| 1608 | + return |
| 1609 | + |
| 1610 | + url = 'https://api.github.com/repos/coderholic/pyradio/tags' |
| 1611 | + while True: |
| 1612 | + if stop(): |
| 1613 | + if logger.isEnabledFor(logging.DEBUG): |
| 1614 | + logger.debug('detectUpdateThread: Asked to stop. Stoping...') |
| 1615 | + break |
| 1616 | + if version_info < (3, 0): |
| 1617 | + try: |
| 1618 | + last_tag = urlopen(url).read(300) |
| 1619 | + except: |
| 1620 | + last_tag = None |
| 1621 | + else: |
| 1622 | + try: |
| 1623 | + with urlopen(url) as https_response: |
| 1624 | + last_tag = https_response.read(300) |
| 1625 | + except: |
| 1626 | + last_tag = None |
| 1627 | + |
| 1628 | + if stop(): |
| 1629 | + if logger.isEnabledFor(logging.DEBUG): |
| 1630 | + logger.debug('detectUpdateThread: Asked to stop. Stoping...') |
| 1631 | + break |
| 1632 | + if last_tag: |
| 1633 | + connection_fail_count = 0 |
| 1634 | + x = str(last_tag).split('"name":"') |
| 1635 | + last_tag = x[1].split('"')[0] |
| 1636 | + #last_tag = '0.9.9' |
| 1637 | + if logger.isEnabledFor(logging.DEBUG): |
| 1638 | + logger.debug('detectUpdateThread: Upstream version found: {}'.format(last_tag)) |
| 1639 | + if this_version == last_tag: |
| 1640 | + if logger.isEnabledFor(logging.DEBUG): |
| 1641 | + logger.debug('detectUpdateThread: No update found. Exiting...') |
| 1642 | + break |
| 1643 | + else: |
| 1644 | + existing_version = to_ver(this_version) |
| 1645 | + new_version = to_ver(last_tag) |
| 1646 | + if existing_version < new_version: |
| 1647 | + if stop(): |
| 1648 | + if logger.isEnabledFor(logging.DEBUG): |
| 1649 | + logger.debug('detectUpdateThread: Asked to stop. Stoping...') |
| 1650 | + break |
| 1651 | + # remove all existing date files |
| 1652 | + clean_date_files(files, -1) |
| 1653 | + ############ |
| 1654 | + #delay(5 , stop) |
| 1655 | + delay(random.randint(120, 300), stop) |
| 1656 | + if stop(): |
| 1657 | + if logger.isEnabledFor(logging.DEBUG): |
| 1658 | + logger.debug('detectUpdateThread: Asked to stop. Stoping...') |
| 1659 | + break |
| 1660 | + # set new verion |
| 1661 | + if logger.isEnabledFor(logging.INFO): |
| 1662 | + logger.info('detectUpdateThread: Update available: {}'.format(last_tag)) |
| 1663 | + a_lock.acquire() |
| 1664 | + self._update_version = last_tag |
| 1665 | + a_lock.release() |
| 1666 | + while True: |
| 1667 | + """ Wait until self._update_version becomes '' |
| 1668 | + which means that notification window has been |
| 1669 | + displayed. Then create date file and exit. |
| 1670 | + If asked to terminate, do not write date file """ |
| 1671 | + ########################33 |
| 1672 | + #delay(5, stop) |
| 1673 | + delay(60, stop) |
| 1674 | + if stop(): |
| 1675 | + if logger.isEnabledFor(logging.DEBUG): |
| 1676 | + logger.debug('detectUpdateThread: Asked to stop. Stoping but not writing date file...') |
| 1677 | + return |
| 1678 | + a_lock.acquire() |
| 1679 | + if self._update_version == '': |
| 1680 | + a_lock.release() |
| 1681 | + # create today's date file |
| 1682 | + create_tadays_date_file(a_path) |
| 1683 | + if logger.isEnabledFor(logging.INFO): |
| 1684 | + logger.info('detectUpdateThread: Terminating after notification... I will check again in 3 days') |
| 1685 | + return |
| 1686 | + a_lock.release() |
| 1687 | + else: |
| 1688 | + if logger.isEnabledFor(logging.ERROR): |
| 1689 | + logger.error('detectUpdateThread: Ahead of upstream? (current version: {0}, upstream version: {1})'.format(this_version, last_tag)) |
| 1690 | + break |
| 1691 | + |
| 1692 | + else: |
| 1693 | + if logger.isEnabledFor(logging.DEBUG): |
| 1694 | + logger.debug('detectUpdateThread: Error: Cannot get upstream version!!!') |
| 1695 | + connection_fail_count += 1 |
| 1696 | + if connection_fail_count > 4: |
| 1697 | + if logger.isEnabledFor(logging.DEBUG): |
| 1698 | + logger.debug('detectUpdateThread: Error: Too many connection failures. Exiting') |
| 1699 | + break |
| 1700 | + delay(60, stop) |
| 1701 | + |
| 1702 | + |
| 1703 | + |
1513 | 1704 | def keypress(self, char):
|
1514 | 1705 |
|
1515 | 1706 | if self.operation_mode == CONFIG_SAVE_ERROR_MODE:
|
@@ -2595,13 +2786,4 @@ def keypress(self, char):
|
2595 | 2786 | self.selections[self.operation_mode] = (self.selection, self.startPos, self.playing, self._cnf.playlists)
|
2596 | 2787 | self.refreshBody()
|
2597 | 2788 |
|
2598 |
| - def detectUpdateThread(self, *args): |
2599 |
| - """ a thread to check if an update is available |
2600 |
| -
|
2601 |
| - arg[0]: config dir |
2602 |
| - arg[1]: lock |
2603 |
| - """ |
2604 |
| - while True: |
2605 |
| - |
2606 |
| - pass |
2607 | 2789 | # pymode:lint_ignore=W901
|
0 commit comments