mirror of
https://github.com/chibicitiberiu/ytsm.git
synced 2024-02-24 05:43:31 +00:00
Integrated pytaw library for youtube API.
This commit is contained in:
parent
6dd63b078f
commit
0fb09b00da
12
.idea/dataSources.local.xml
generated
12
.idea/dataSources.local.xml
generated
@ -2,7 +2,17 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="dataSourceStorageLocal">
|
<component name="dataSourceStorageLocal">
|
||||||
<data-source name="Django default" uuid="2dac2136-d902-4d27-8789-9371934602fd">
|
<data-source name="Django default" uuid="2dac2136-d902-4d27-8789-9371934602fd">
|
||||||
<database-info product="SQLite" version="3.20.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.20.1.1" family="SQLITE" exact-version="3.20.1" />
|
<database-info product="SQLite" version="3.25.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.25.1" family="SQLITE" exact-version="3.25.1">
|
||||||
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
</database-info>
|
||||||
|
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||||
|
<auth-required>false</auth-required>
|
||||||
|
<introspection-schemas>*:@</introspection-schemas>
|
||||||
|
</data-source>
|
||||||
|
<data-source name="db" uuid="77df9da5-0b97-445e-a895-744ef8257a74">
|
||||||
|
<database-info product="SQLite" version="3.25.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.25.1" family="SQLITE" exact-version="3.25.1">
|
||||||
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
</database-info>
|
||||||
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||||
<auth-required>false</auth-required>
|
<auth-required>false</auth-required>
|
||||||
<introspection-schemas>*:@</introspection-schemas>
|
<introspection-schemas>*:@</introspection-schemas>
|
||||||
|
17
.idea/dataSources.xml
generated
17
.idea/dataSources.xml
generated
@ -12,5 +12,22 @@
|
|||||||
<property name="enable_load_extension" value="true" />
|
<property name="enable_load_extension" value="true" />
|
||||||
</driver-properties>
|
</driver-properties>
|
||||||
</data-source>
|
</data-source>
|
||||||
|
<data-source source="LOCAL" name="db" uuid="77df9da5-0b97-445e-a895-744ef8257a74">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db.sqlite3</jdbc-url>
|
||||||
|
<driver-properties>
|
||||||
|
<property name="enable_load_extension" value="true" />
|
||||||
|
</driver-properties>
|
||||||
|
<libraries>
|
||||||
|
<library>
|
||||||
|
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.25.1/sqlite-jdbc-3.25.1.jar</url>
|
||||||
|
</library>
|
||||||
|
<library>
|
||||||
|
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.25.1/license.txt</url>
|
||||||
|
</library>
|
||||||
|
</libraries>
|
||||||
|
</data-source>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<dataSource name="Django default">
|
<dataSource name="Django default">
|
||||||
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.10">
|
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.11">
|
||||||
<root id="1">
|
<root id="1">
|
||||||
<ServerVersion>3.20.1</ServerVersion>
|
<ServerVersion>3.25.1</ServerVersion>
|
||||||
</root>
|
</root>
|
||||||
<schema id="2" parent="1" name="main">
|
<schema id="2" parent="1" name="main">
|
||||||
<Current>1</Current>
|
<Current>1</Current>
|
||||||
@ -11,868 +11,28 @@
|
|||||||
<collation id="3" parent="1" name="BINARY"/>
|
<collation id="3" parent="1" name="BINARY"/>
|
||||||
<collation id="4" parent="1" name="NOCASE"/>
|
<collation id="4" parent="1" name="NOCASE"/>
|
||||||
<collation id="5" parent="1" name="RTRIM"/>
|
<collation id="5" parent="1" name="RTRIM"/>
|
||||||
<table id="6" parent="2" name="YtManagerApp_channel"/>
|
<table id="6" parent="2" name="sqlite_master">
|
||||||
<table id="7" parent="2" name="YtManagerApp_subscription"/>
|
|
||||||
<table id="8" parent="2" name="YtManagerApp_subscriptionfolder"/>
|
|
||||||
<table id="9" parent="2" name="YtManagerApp_usersettings"/>
|
|
||||||
<table id="10" parent="2" name="YtManagerApp_video"/>
|
|
||||||
<table id="11" parent="2" name="auth_group"/>
|
|
||||||
<table id="12" parent="2" name="auth_group_permissions"/>
|
|
||||||
<table id="13" parent="2" name="auth_permission"/>
|
|
||||||
<table id="14" parent="2" name="auth_user"/>
|
|
||||||
<table id="15" parent="2" name="auth_user_groups"/>
|
|
||||||
<table id="16" parent="2" name="auth_user_user_permissions"/>
|
|
||||||
<table id="17" parent="2" name="django_admin_log"/>
|
|
||||||
<table id="18" parent="2" name="django_content_type"/>
|
|
||||||
<table id="19" parent="2" name="django_migrations"/>
|
|
||||||
<table id="20" parent="2" name="django_session"/>
|
|
||||||
<table id="21" parent="2" name="sqlite_master">
|
|
||||||
<System>1</System>
|
<System>1</System>
|
||||||
</table>
|
</table>
|
||||||
<table id="22" parent="2" name="sqlite_sequence">
|
<column id="7" parent="6" name="type">
|
||||||
<System>1</System>
|
|
||||||
</table>
|
|
||||||
<column id="23" parent="6" name="id">
|
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>integer|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
</column>
|
||||||
<column id="24" parent="6" name="channel_id">
|
<column id="8" parent="6" name="name">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
</column>
|
||||||
<column id="25" parent="6" name="username">
|
<column id="9" parent="6" name="tbl_name">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="26" parent="6" name="custom_url">
|
<column id="10" parent="6" name="rootpage">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>int|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="27" parent="6" name="name">
|
<column id="11" parent="6" name="sql">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="28" parent="6" name="description">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="29" parent="6" name="icon_default">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="30" parent="6" name="icon_best">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="31" parent="6" name="upload_playlist_id">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="32" parent="6" name="sqlite_autoindex_YtManagerApp_channel_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>channel_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="33" parent="6" name="sqlite_autoindex_YtManagerApp_channel_2">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>username</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="34" parent="6" name="sqlite_autoindex_YtManagerApp_channel_3">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>custom_url</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="35" parent="6">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<key id="36" parent="6">
|
|
||||||
<ColNames>channel_id</ColNames>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<key id="37" parent="6">
|
|
||||||
<ColNames>username</ColNames>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_2</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<key id="38" parent="6">
|
|
||||||
<ColNames>custom_url</ColNames>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_3</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="39" parent="7" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="40" parent="7" name="name">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="41" parent="7" name="playlist_id">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="42" parent="7" name="description">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="43" parent="7" name="icon_default">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="44" parent="7" name="icon_best">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="45" parent="7" name="auto_download">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="46" parent="7" name="download_limit">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="47" parent="7" name="download_order">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="48" parent="7" name="manager_delete_after_watched">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="49" parent="7" name="channel_id">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="50" parent="7" name="parent_folder_id">
|
|
||||||
<Position>12</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="51" parent="7" name="user_id">
|
|
||||||
<Position>13</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="52" parent="7" name="sqlite_autoindex_YtManagerApp_subscription_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>playlist_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="53" parent="7" name="YtManagerApp_subscription_channel_id_b83c6f21">
|
|
||||||
<ColNames>channel_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<index id="54" parent="7" name="YtManagerApp_subscription_parent_folder_id_c4c64c21">
|
|
||||||
<ColNames>parent_folder_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<index id="55" parent="7" name="YtManagerApp_subscription_user_id_9d38617d">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="56" parent="7">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<key id="57" parent="7">
|
|
||||||
<ColNames>playlist_id</ColNames>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_subscription_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="58" parent="7">
|
|
||||||
<ColNames>channel_id</ColNames>
|
|
||||||
<RefTableName>YtManagerApp_channel</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<foreign-key id="59" parent="7">
|
|
||||||
<ColNames>parent_folder_id</ColNames>
|
|
||||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<foreign-key id="60" parent="7">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<RefTableName>auth_user</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="61" parent="8" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="62" parent="8" name="name">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="63" parent="8" name="user_id">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="64" parent="8" name="parent_id">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<index id="65" parent="8" name="YtManagerApp_subscriptionfolder_user_id_6fb12da0">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<index id="66" parent="8" name="YtManagerApp_subscriptionfolder_parent_id_bd5f4bc1">
|
|
||||||
<ColNames>parent_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="67" parent="8">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="68" parent="8">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<RefTableName>auth_user</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<foreign-key id="69" parent="8">
|
|
||||||
<ColNames>parent_id</ColNames>
|
|
||||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="70" parent="9" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="71" parent="9" name="mark_deleted_as_watched">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="72" parent="9" name="delete_watched">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="73" parent="9" name="auto_download">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="74" parent="9" name="download_global_limit">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="75" parent="9" name="download_subscription_limit">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="76" parent="9" name="download_order">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="77" parent="9" name="download_path">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="78" parent="9" name="download_file_pattern">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="79" parent="9" name="download_format">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="80" parent="9" name="download_subtitles">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="81" parent="9" name="download_autogenerated_subtitles">
|
|
||||||
<Position>12</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="82" parent="9" name="download_subtitles_all">
|
|
||||||
<Position>13</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="83" parent="9" name="download_subtitles_langs">
|
|
||||||
<Position>14</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="84" parent="9" name="download_subtitles_format">
|
|
||||||
<Position>15</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="85" parent="9" name="user_id">
|
|
||||||
<Position>16</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="86" parent="9" name="sqlite_autoindex_YtManagerApp_usersettings_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="87" parent="9">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<key id="88" parent="9">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_usersettings_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="89" parent="9">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<RefTableName>auth_user</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="90" parent="10" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="91" parent="10" name="video_id">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="92" parent="10" name="name">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="93" parent="10" name="description">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="94" parent="10" name="watched">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="95" parent="10" name="downloaded_path">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="96" parent="10" name="playlist_index">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="97" parent="10" name="publish_date">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>datetime|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="98" parent="10" name="icon_default">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="99" parent="10" name="icon_best">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="100" parent="10" name="subscription_id">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="101" parent="10" name="YtManagerApp_video_subscription_id_720d4227">
|
|
||||||
<ColNames>subscription_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="102" parent="10">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="103" parent="10">
|
|
||||||
<ColNames>subscription_id</ColNames>
|
|
||||||
<RefTableName>YtManagerApp_subscription</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="104" parent="11" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="105" parent="11" name="name">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>varchar(80)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="106" parent="11" name="sqlite_autoindex_auth_group_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>name</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="107" parent="11">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<key id="108" parent="11">
|
|
||||||
<ColNames>name</ColNames>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_auth_group_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="109" parent="12" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="110" parent="12" name="group_id">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="111" parent="12" name="permission_id">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="112" parent="12" name="auth_group_permissions_group_id_permission_id_0cd325b0_uniq">
|
|
||||||
<ColNames>group_id
|
|
||||||
permission_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="113" parent="12" name="auth_group_permissions_group_id_b120cbf9">
|
|
||||||
<ColNames>group_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<index id="114" parent="12" name="auth_group_permissions_permission_id_84c5c92e">
|
|
||||||
<ColNames>permission_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="115" parent="12">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="116" parent="12">
|
|
||||||
<ColNames>group_id</ColNames>
|
|
||||||
<RefTableName>auth_group</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<foreign-key id="117" parent="12">
|
|
||||||
<ColNames>permission_id</ColNames>
|
|
||||||
<RefTableName>auth_permission</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="118" parent="13" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="119" parent="13" name="content_type_id">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="120" parent="13" name="codename">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>varchar(100)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="121" parent="13" name="name">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>varchar(255)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="122" parent="13" name="auth_permission_content_type_id_codename_01ab375a_uniq">
|
|
||||||
<ColNames>content_type_id
|
|
||||||
codename</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="123" parent="13" name="auth_permission_content_type_id_2f476e4b">
|
|
||||||
<ColNames>content_type_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="124" parent="13">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="125" parent="13">
|
|
||||||
<ColNames>content_type_id</ColNames>
|
|
||||||
<RefTableName>django_content_type</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="126" parent="14" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="127" parent="14" name="password">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>varchar(128)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="128" parent="14" name="last_login">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>datetime|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="129" parent="14" name="is_superuser">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="130" parent="14" name="username">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>varchar(150)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="131" parent="14" name="first_name">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>varchar(30)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="132" parent="14" name="email">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>varchar(254)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="133" parent="14" name="is_staff">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="134" parent="14" name="is_active">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>bool|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="135" parent="14" name="date_joined">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>datetime|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="136" parent="14" name="last_name">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>varchar(150)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="137" parent="14" name="sqlite_autoindex_auth_user_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>username</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="138" parent="14">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<key id="139" parent="14">
|
|
||||||
<ColNames>username</ColNames>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_auth_user_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="140" parent="15" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="141" parent="15" name="user_id">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="142" parent="15" name="group_id">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="143" parent="15" name="auth_user_groups_user_id_group_id_94350c0c_uniq">
|
|
||||||
<ColNames>user_id
|
|
||||||
group_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="144" parent="15" name="auth_user_groups_user_id_6a12ed8b">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<index id="145" parent="15" name="auth_user_groups_group_id_97559544">
|
|
||||||
<ColNames>group_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="146" parent="15">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="147" parent="15">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<RefTableName>auth_user</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<foreign-key id="148" parent="15">
|
|
||||||
<ColNames>group_id</ColNames>
|
|
||||||
<RefTableName>auth_group</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="149" parent="16" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="150" parent="16" name="user_id">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="151" parent="16" name="permission_id">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="152" parent="16" name="auth_user_user_permissions_user_id_permission_id_14a6b632_uniq">
|
|
||||||
<ColNames>user_id
|
|
||||||
permission_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="153" parent="16" name="auth_user_user_permissions_user_id_a95ead1b">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<index id="154" parent="16" name="auth_user_user_permissions_permission_id_1fbb5f2c">
|
|
||||||
<ColNames>permission_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="155" parent="16">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="156" parent="16">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<RefTableName>auth_user</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<foreign-key id="157" parent="16">
|
|
||||||
<ColNames>permission_id</ColNames>
|
|
||||||
<RefTableName>auth_permission</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="158" parent="17" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="159" parent="17" name="action_time">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>datetime|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="160" parent="17" name="object_id">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="161" parent="17" name="object_repr">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>varchar(200)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="162" parent="17" name="change_message">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="163" parent="17" name="content_type_id">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="164" parent="17" name="user_id">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="165" parent="17" name="action_flag">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>smallint unsigned|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="166" parent="17" name="django_admin_log_content_type_id_c4bce8eb">
|
|
||||||
<ColNames>content_type_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<index id="167" parent="17" name="django_admin_log_user_id_c564eba6">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="168" parent="17">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<foreign-key id="169" parent="17">
|
|
||||||
<ColNames>content_type_id</ColNames>
|
|
||||||
<RefTableName>django_content_type</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<foreign-key id="170" parent="17">
|
|
||||||
<ColNames>user_id</ColNames>
|
|
||||||
<RefTableName>auth_user</RefTableName>
|
|
||||||
<RefColNames>id</RefColNames>
|
|
||||||
<Deferrable>1</Deferrable>
|
|
||||||
<InitiallyDeferred>1</InitiallyDeferred>
|
|
||||||
</foreign-key>
|
|
||||||
<column id="171" parent="18" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="172" parent="18" name="app_label">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>varchar(100)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="173" parent="18" name="model">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>varchar(100)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="174" parent="18" name="django_content_type_app_label_model_76bd3d3b_uniq">
|
|
||||||
<ColNames>app_label
|
|
||||||
model</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="175" parent="18">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<column id="176" parent="19" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="177" parent="19" name="app">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>varchar(255)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="178" parent="19" name="name">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>varchar(255)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="179" parent="19" name="applied">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>datetime|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<key id="180" parent="19">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
<column id="181" parent="20" name="session_key">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>varchar(40)|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="182" parent="20" name="session_data">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="183" parent="20" name="expire_date">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>datetime|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="184" parent="20" name="sqlite_autoindex_django_session_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>session_key</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="185" parent="20" name="django_session_expire_date_a5c62663">
|
|
||||||
<ColNames>expire_date</ColNames>
|
|
||||||
<ColumnCollations></ColumnCollations>
|
|
||||||
</index>
|
|
||||||
<key id="186" parent="20">
|
|
||||||
<ColNames>session_key</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_django_session_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="187" parent="21" name="type">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="188" parent="21" name="name">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="189" parent="21" name="tbl_name">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="190" parent="21" name="rootpage">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>integer|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="191" parent="21" name="sql">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="192" parent="22" name="name">
|
|
||||||
<Position>1</Position>
|
|
||||||
</column>
|
|
||||||
<column id="193" parent="22" name="seq">
|
|
||||||
<Position>2</Position>
|
|
||||||
</column>
|
</column>
|
||||||
</database-model>
|
</database-model>
|
||||||
</dataSource>
|
</dataSource>
|
883
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74.xml
generated
Normal file
883
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74.xml
generated
Normal file
@ -0,0 +1,883 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<dataSource name="db">
|
||||||
|
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.11">
|
||||||
|
<root id="1">
|
||||||
|
<ServerVersion>3.25.1</ServerVersion>
|
||||||
|
</root>
|
||||||
|
<schema id="2" parent="1" name="main">
|
||||||
|
<Current>1</Current>
|
||||||
|
<Visible>1</Visible>
|
||||||
|
</schema>
|
||||||
|
<collation id="3" parent="1" name="BINARY"/>
|
||||||
|
<collation id="4" parent="1" name="NOCASE"/>
|
||||||
|
<collation id="5" parent="1" name="RTRIM"/>
|
||||||
|
<table id="6" parent="2" name="YtManagerApp_channel"/>
|
||||||
|
<table id="7" parent="2" name="YtManagerApp_subscription"/>
|
||||||
|
<table id="8" parent="2" name="YtManagerApp_subscriptionfolder"/>
|
||||||
|
<table id="9" parent="2" name="YtManagerApp_usersettings"/>
|
||||||
|
<table id="10" parent="2" name="YtManagerApp_video"/>
|
||||||
|
<table id="11" parent="2" name="auth_group"/>
|
||||||
|
<table id="12" parent="2" name="auth_group_permissions"/>
|
||||||
|
<table id="13" parent="2" name="auth_permission"/>
|
||||||
|
<table id="14" parent="2" name="auth_user"/>
|
||||||
|
<table id="15" parent="2" name="auth_user_groups"/>
|
||||||
|
<table id="16" parent="2" name="auth_user_user_permissions"/>
|
||||||
|
<table id="17" parent="2" name="django_admin_log"/>
|
||||||
|
<table id="18" parent="2" name="django_content_type"/>
|
||||||
|
<table id="19" parent="2" name="django_migrations"/>
|
||||||
|
<table id="20" parent="2" name="django_session"/>
|
||||||
|
<table id="21" parent="2" name="sqlite_master">
|
||||||
|
<System>1</System>
|
||||||
|
</table>
|
||||||
|
<table id="22" parent="2" name="sqlite_sequence">
|
||||||
|
<System>1</System>
|
||||||
|
</table>
|
||||||
|
<column id="23" parent="6" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="24" parent="6" name="channel_id">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="25" parent="6" name="username">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="26" parent="6" name="custom_url">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="27" parent="6" name="name">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="28" parent="6" name="description">
|
||||||
|
<Position>6</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="29" parent="6" name="icon_default">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="30" parent="6" name="icon_best">
|
||||||
|
<Position>8</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="31" parent="6" name="upload_playlist_id">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="32" parent="6" name="sqlite_autoindex_YtManagerApp_channel_1">
|
||||||
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>channel_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<index id="33" parent="6" name="sqlite_autoindex_YtManagerApp_channel_2">
|
||||||
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>username</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<index id="34" parent="6" name="sqlite_autoindex_YtManagerApp_channel_3">
|
||||||
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>custom_url</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<key id="35" parent="6">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<key id="36" parent="6">
|
||||||
|
<ColNames>channel_id</ColNames>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_1</UnderlyingIndexName>
|
||||||
|
</key>
|
||||||
|
<key id="37" parent="6">
|
||||||
|
<ColNames>username</ColNames>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_2</UnderlyingIndexName>
|
||||||
|
</key>
|
||||||
|
<key id="38" parent="6">
|
||||||
|
<ColNames>custom_url</ColNames>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_3</UnderlyingIndexName>
|
||||||
|
</key>
|
||||||
|
<column id="39" parent="7" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="40" parent="7" name="name">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>varchar(1024)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="41" parent="7" name="playlist_id">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>varchar(128)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="42" parent="7" name="description">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="43" parent="7" name="icon_default">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>varchar(1024)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="44" parent="7" name="icon_best">
|
||||||
|
<Position>6</Position>
|
||||||
|
<DataType>varchar(1024)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="45" parent="7" name="auto_download">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="46" parent="7" name="download_limit">
|
||||||
|
<Position>8</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="47" parent="7" name="download_order">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>varchar(128)|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="48" parent="7" name="channel_id">
|
||||||
|
<Position>10</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="49" parent="7" name="parent_folder_id">
|
||||||
|
<Position>11</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="50" parent="7" name="user_id">
|
||||||
|
<Position>12</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="51" parent="7" name="delete_after_watched">
|
||||||
|
<Position>13</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<index id="52" parent="7" name="YtManagerApp_subscription_channel_id_b83c6f21">
|
||||||
|
<ColNames>channel_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<index id="53" parent="7" name="YtManagerApp_subscription_parent_folder_id_c4c64c21">
|
||||||
|
<ColNames>parent_folder_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<index id="54" parent="7" name="YtManagerApp_subscription_user_id_9d38617d">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="55" parent="7">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="56" parent="7">
|
||||||
|
<ColNames>channel_id</ColNames>
|
||||||
|
<RefTableName>YtManagerApp_channel</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<foreign-key id="57" parent="7">
|
||||||
|
<ColNames>parent_folder_id</ColNames>
|
||||||
|
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<foreign-key id="58" parent="7">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<RefTableName>auth_user</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="59" parent="8" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="60" parent="8" name="user_id">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="61" parent="8" name="name">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>varchar(250)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="62" parent="8" name="parent_id">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<index id="63" parent="8" name="YtManagerApp_subscriptionfolder_user_id_6fb12da0">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<index id="64" parent="8" name="YtManagerApp_subscriptionfolder_parent_id_bd5f4bc1">
|
||||||
|
<ColNames>parent_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="65" parent="8">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="66" parent="8">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<RefTableName>auth_user</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<foreign-key id="67" parent="8">
|
||||||
|
<ColNames>parent_id</ColNames>
|
||||||
|
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="68" parent="9" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="69" parent="9" name="delete_watched">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="70" parent="9" name="auto_download">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="71" parent="9" name="download_global_limit">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="72" parent="9" name="download_subscription_limit">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="73" parent="9" name="download_order">
|
||||||
|
<Position>6</Position>
|
||||||
|
<DataType>varchar(100)|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="74" parent="9" name="download_path">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>varchar(1024)|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="75" parent="9" name="download_file_pattern">
|
||||||
|
<Position>8</Position>
|
||||||
|
<DataType>varchar(1024)|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="76" parent="9" name="download_format">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>varchar(256)|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="77" parent="9" name="download_subtitles">
|
||||||
|
<Position>10</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="78" parent="9" name="download_autogenerated_subtitles">
|
||||||
|
<Position>11</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="79" parent="9" name="download_subtitles_all">
|
||||||
|
<Position>12</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="80" parent="9" name="download_subtitles_langs">
|
||||||
|
<Position>13</Position>
|
||||||
|
<DataType>varchar(250)|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="81" parent="9" name="download_subtitles_format">
|
||||||
|
<Position>14</Position>
|
||||||
|
<DataType>varchar(100)|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="82" parent="9" name="user_id">
|
||||||
|
<Position>15</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="83" parent="9" name="mark_deleted_as_watched">
|
||||||
|
<Position>16</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<index id="84" parent="9" name="sqlite_autoindex_YtManagerApp_usersettings_1">
|
||||||
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<key id="85" parent="9">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<key id="86" parent="9">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_usersettings_1</UnderlyingIndexName>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="87" parent="9">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<RefTableName>auth_user</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="88" parent="10" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="89" parent="10" name="video_id">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="90" parent="10" name="name">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="91" parent="10" name="description">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="92" parent="10" name="watched">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="93" parent="10" name="downloaded_path">
|
||||||
|
<Position>6</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="94" parent="10" name="playlist_index">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="95" parent="10" name="publish_date">
|
||||||
|
<Position>8</Position>
|
||||||
|
<DataType>datetime|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="96" parent="10" name="icon_default">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="97" parent="10" name="icon_best">
|
||||||
|
<Position>10</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="98" parent="10" name="subscription_id">
|
||||||
|
<Position>11</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="99" parent="10" name="rating">
|
||||||
|
<Position>12</Position>
|
||||||
|
<DataType>real|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="100" parent="10" name="uploader_name">
|
||||||
|
<Position>13</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="101" parent="10" name="views">
|
||||||
|
<Position>14</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="102" parent="10" name="YtManagerApp_video_subscription_id_720d4227">
|
||||||
|
<ColNames>subscription_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="103" parent="10">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="104" parent="10">
|
||||||
|
<ColNames>subscription_id</ColNames>
|
||||||
|
<RefTableName>YtManagerApp_subscription</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="105" parent="11" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="106" parent="11" name="name">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>varchar(80)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="107" parent="11" name="sqlite_autoindex_auth_group_1">
|
||||||
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>name</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<key id="108" parent="11">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<key id="109" parent="11">
|
||||||
|
<ColNames>name</ColNames>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_auth_group_1</UnderlyingIndexName>
|
||||||
|
</key>
|
||||||
|
<column id="110" parent="12" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="111" parent="12" name="group_id">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="112" parent="12" name="permission_id">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="113" parent="12" name="auth_group_permissions_group_id_permission_id_0cd325b0_uniq">
|
||||||
|
<ColNames>group_id
|
||||||
|
permission_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<index id="114" parent="12" name="auth_group_permissions_group_id_b120cbf9">
|
||||||
|
<ColNames>group_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<index id="115" parent="12" name="auth_group_permissions_permission_id_84c5c92e">
|
||||||
|
<ColNames>permission_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="116" parent="12">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="117" parent="12">
|
||||||
|
<ColNames>group_id</ColNames>
|
||||||
|
<RefTableName>auth_group</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<foreign-key id="118" parent="12">
|
||||||
|
<ColNames>permission_id</ColNames>
|
||||||
|
<RefTableName>auth_permission</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="119" parent="13" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="120" parent="13" name="content_type_id">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="121" parent="13" name="codename">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>varchar(100)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="122" parent="13" name="name">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>varchar(255)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="123" parent="13" name="auth_permission_content_type_id_codename_01ab375a_uniq">
|
||||||
|
<ColNames>content_type_id
|
||||||
|
codename</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<index id="124" parent="13" name="auth_permission_content_type_id_2f476e4b">
|
||||||
|
<ColNames>content_type_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="125" parent="13">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="126" parent="13">
|
||||||
|
<ColNames>content_type_id</ColNames>
|
||||||
|
<RefTableName>django_content_type</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="127" parent="14" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="128" parent="14" name="password">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>varchar(128)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="129" parent="14" name="last_login">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>datetime|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="130" parent="14" name="is_superuser">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="131" parent="14" name="username">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>varchar(150)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="132" parent="14" name="first_name">
|
||||||
|
<Position>6</Position>
|
||||||
|
<DataType>varchar(30)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="133" parent="14" name="email">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>varchar(254)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="134" parent="14" name="is_staff">
|
||||||
|
<Position>8</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="135" parent="14" name="is_active">
|
||||||
|
<Position>9</Position>
|
||||||
|
<DataType>bool|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="136" parent="14" name="date_joined">
|
||||||
|
<Position>10</Position>
|
||||||
|
<DataType>datetime|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="137" parent="14" name="last_name">
|
||||||
|
<Position>11</Position>
|
||||||
|
<DataType>varchar(150)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="138" parent="14" name="sqlite_autoindex_auth_user_1">
|
||||||
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>username</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<key id="139" parent="14">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<key id="140" parent="14">
|
||||||
|
<ColNames>username</ColNames>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_auth_user_1</UnderlyingIndexName>
|
||||||
|
</key>
|
||||||
|
<column id="141" parent="15" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="142" parent="15" name="user_id">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="143" parent="15" name="group_id">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="144" parent="15" name="auth_user_groups_user_id_group_id_94350c0c_uniq">
|
||||||
|
<ColNames>user_id
|
||||||
|
group_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<index id="145" parent="15" name="auth_user_groups_user_id_6a12ed8b">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<index id="146" parent="15" name="auth_user_groups_group_id_97559544">
|
||||||
|
<ColNames>group_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="147" parent="15">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="148" parent="15">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<RefTableName>auth_user</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<foreign-key id="149" parent="15">
|
||||||
|
<ColNames>group_id</ColNames>
|
||||||
|
<RefTableName>auth_group</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="150" parent="16" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="151" parent="16" name="user_id">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="152" parent="16" name="permission_id">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="153" parent="16" name="auth_user_user_permissions_user_id_permission_id_14a6b632_uniq">
|
||||||
|
<ColNames>user_id
|
||||||
|
permission_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<index id="154" parent="16" name="auth_user_user_permissions_user_id_a95ead1b">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<index id="155" parent="16" name="auth_user_user_permissions_permission_id_1fbb5f2c">
|
||||||
|
<ColNames>permission_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="156" parent="16">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="157" parent="16">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<RefTableName>auth_user</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<foreign-key id="158" parent="16">
|
||||||
|
<ColNames>permission_id</ColNames>
|
||||||
|
<RefTableName>auth_permission</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="159" parent="17" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="160" parent="17" name="action_time">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>datetime|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="161" parent="17" name="object_id">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="162" parent="17" name="object_repr">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>varchar(200)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="163" parent="17" name="change_message">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="164" parent="17" name="content_type_id">
|
||||||
|
<Position>6</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="165" parent="17" name="user_id">
|
||||||
|
<Position>7</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="166" parent="17" name="action_flag">
|
||||||
|
<Position>8</Position>
|
||||||
|
<DataType>smallint unsigned|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="167" parent="17" name="django_admin_log_content_type_id_c4bce8eb">
|
||||||
|
<ColNames>content_type_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<index id="168" parent="17" name="django_admin_log_user_id_c564eba6">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="169" parent="17">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<foreign-key id="170" parent="17">
|
||||||
|
<ColNames>content_type_id</ColNames>
|
||||||
|
<RefTableName>django_content_type</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<foreign-key id="171" parent="17">
|
||||||
|
<ColNames>user_id</ColNames>
|
||||||
|
<RefTableName>auth_user</RefTableName>
|
||||||
|
<RefColNames>id</RefColNames>
|
||||||
|
<Deferrable>1</Deferrable>
|
||||||
|
<InitiallyDeferred>1</InitiallyDeferred>
|
||||||
|
</foreign-key>
|
||||||
|
<column id="172" parent="18" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="173" parent="18" name="app_label">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>varchar(100)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="174" parent="18" name="model">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>varchar(100)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="175" parent="18" name="django_content_type_app_label_model_76bd3d3b_uniq">
|
||||||
|
<ColNames>app_label
|
||||||
|
model</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<key id="176" parent="18">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<column id="177" parent="19" name="id">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>integer|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
|
</column>
|
||||||
|
<column id="178" parent="19" name="app">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>varchar(255)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="179" parent="19" name="name">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>varchar(255)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="180" parent="19" name="applied">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>datetime|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<key id="181" parent="19">
|
||||||
|
<ColNames>id</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
</key>
|
||||||
|
<column id="182" parent="20" name="session_key">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>varchar(40)|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="183" parent="20" name="session_data">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<column id="184" parent="20" name="expire_date">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>datetime|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
|
</column>
|
||||||
|
<index id="185" parent="20" name="sqlite_autoindex_django_session_1">
|
||||||
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>session_key</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<index id="186" parent="20" name="django_session_expire_date_a5c62663">
|
||||||
|
<ColNames>expire_date</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
</index>
|
||||||
|
<key id="187" parent="20">
|
||||||
|
<ColNames>session_key</ColNames>
|
||||||
|
<Primary>1</Primary>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_django_session_1</UnderlyingIndexName>
|
||||||
|
</key>
|
||||||
|
<column id="188" parent="21" name="type">
|
||||||
|
<Position>1</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="189" parent="21" name="name">
|
||||||
|
<Position>2</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="190" parent="21" name="tbl_name">
|
||||||
|
<Position>3</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="191" parent="21" name="rootpage">
|
||||||
|
<Position>4</Position>
|
||||||
|
<DataType>int|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="192" parent="21" name="sql">
|
||||||
|
<Position>5</Position>
|
||||||
|
<DataType>text|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="193" parent="22" name="name">
|
||||||
|
<Position>1</Position>
|
||||||
|
</column>
|
||||||
|
<column id="194" parent="22" name="seq">
|
||||||
|
<Position>2</Position>
|
||||||
|
</column>
|
||||||
|
</database-model>
|
||||||
|
</dataSource>
|
2
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74/storage_v2/_src_/schema/main.uQUzAA.meta
generated
Normal file
2
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74/storage_v2/_src_/schema/main.uQUzAA.meta
generated
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#n:main
|
||||||
|
!<md> [0, 0, null, null, -2147483648, -2147483648]
|
565
.idea/workspace.xml
generated
565
.idea/workspace.xml
generated
@ -2,10 +2,39 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="245751b6-c863-4572-8723-8499964fe105" name="Default Changelist" comment="">
|
<list default="true" id="245751b6-c863-4572-8723-8499964fe105" name="Default Changelist" comment="">
|
||||||
<change afterPath="$PROJECT_DIR$/readme.md" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/YtManagerApp/migrations/0007_auto_20181029_1638.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/__init__.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/.gitignore" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/.pytaw.conf" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/README.md" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/__init__.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/Makefile" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/conf.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/index.rst" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/make.bat" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/main_test.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/pytaw/__init__.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/pytaw/utils.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/pytaw/youtube.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/setup.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/tests/__init__.py" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/external/pytaw/tests/test_pytaw.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.local.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources/2dac2136-d902-4d27-8789-9371934602fd.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources/2dac2136-d902-4d27-8789-9371934602fd.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/youtube-channel-manager.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/youtube-channel-manager.iml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/management.py" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/videos.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/videos.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/models.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/scheduler.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/scheduler.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/utils/iterutils.py" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/views/index.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/views/index.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/YtManagerApp/views/index.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/views/index.py" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/config/config.ini" beforeDir="false" afterPath="$PROJECT_DIR$/config/config.ini" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/readme.md" beforeDir="false" afterPath="$PROJECT_DIR$/readme.md" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
@ -32,19 +61,23 @@
|
|||||||
<expand>
|
<expand>
|
||||||
<path>
|
<path>
|
||||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
<item name="db" type="feb32156:DbDataSourceImpl" />
|
||||||
</path>
|
</path>
|
||||||
<path>
|
<path>
|
||||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
<item name="db" type="feb32156:DbDataSourceImpl" />
|
||||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||||
</path>
|
</path>
|
||||||
<path>
|
<path>
|
||||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
<item name="db" type="feb32156:DbDataSourceImpl" />
|
||||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||||
<item name="main: schema" type="90513b60:SqliteImplModel$Schema" />
|
<item name="main: schema" type="90513b60:SqliteImplModel$Schema" />
|
||||||
</path>
|
</path>
|
||||||
|
<path>
|
||||||
|
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||||
|
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||||
|
</path>
|
||||||
</expand>
|
</expand>
|
||||||
<select />
|
<select />
|
||||||
</component>
|
</component>
|
||||||
@ -137,51 +170,60 @@
|
|||||||
<usages-collector id="statistics.lifecycle.project">
|
<usages-collector id="statistics.lifecycle.project">
|
||||||
<counts>
|
<counts>
|
||||||
<entry key="project.open.time.10" value="1" />
|
<entry key="project.open.time.10" value="1" />
|
||||||
<entry key="project.opened" value="1" />
|
<entry key="project.open.time.13" value="1" />
|
||||||
|
<entry key="project.opened" value="2" />
|
||||||
</counts>
|
</counts>
|
||||||
</usages-collector>
|
</usages-collector>
|
||||||
<usages-collector id="statistics.file.extensions.open">
|
<usages-collector id="statistics.file.extensions.open">
|
||||||
<counts>
|
<counts>
|
||||||
|
<entry key="auth_group" value="1" />
|
||||||
<entry key="gitignore" value="1" />
|
<entry key="gitignore" value="1" />
|
||||||
<entry key="html" value="1" />
|
<entry key="html" value="1" />
|
||||||
<entry key="md" value="2" />
|
<entry key="ini" value="2" />
|
||||||
<entry key="py" value="3" />
|
<entry key="md" value="5" />
|
||||||
|
<entry key="py" value="35" />
|
||||||
|
<entry key="ytmanagerapp_channel" value="1" />
|
||||||
|
<entry key="ytmanagerapp_subscription" value="2" />
|
||||||
|
<entry key="ytmanagerapp_video" value="3" />
|
||||||
</counts>
|
</counts>
|
||||||
</usages-collector>
|
</usages-collector>
|
||||||
<usages-collector id="statistics.file.types.open">
|
<usages-collector id="statistics.file.types.open">
|
||||||
<counts>
|
<counts>
|
||||||
|
<entry key="Database Element" value="7" />
|
||||||
<entry key="HTML" value="1" />
|
<entry key="HTML" value="1" />
|
||||||
<entry key="Markdown" value="2" />
|
<entry key="Ini" value="2" />
|
||||||
|
<entry key="Markdown" value="5" />
|
||||||
<entry key="PLAIN_TEXT" value="1" />
|
<entry key="PLAIN_TEXT" value="1" />
|
||||||
<entry key="Python" value="3" />
|
<entry key="Python" value="35" />
|
||||||
</counts>
|
</counts>
|
||||||
</usages-collector>
|
</usages-collector>
|
||||||
<usages-collector id="statistics.file.extensions.edit">
|
<usages-collector id="statistics.file.extensions.edit">
|
||||||
<counts>
|
<counts>
|
||||||
<entry key="md" value="464" />
|
<entry key="Django Console" value="155" />
|
||||||
<entry key="py" value="1" />
|
<entry key="ini" value="2" />
|
||||||
<entry key="py@youtube-channel-manager" value="12" />
|
<entry key="md" value="509" />
|
||||||
|
<entry key="py" value="3244" />
|
||||||
|
<entry key="py@youtube-channel-manager" value="41" />
|
||||||
</counts>
|
</counts>
|
||||||
</usages-collector>
|
</usages-collector>
|
||||||
<usages-collector id="statistics.file.types.edit">
|
<usages-collector id="statistics.file.types.edit">
|
||||||
<counts>
|
<counts>
|
||||||
<entry key="CommandLine" value="12" />
|
<entry key="CommandLine" value="23" />
|
||||||
<entry key="Markdown" value="464" />
|
<entry key="Ini" value="2" />
|
||||||
<entry key="Python" value="1" />
|
<entry key="Markdown" value="509" />
|
||||||
|
<entry key="PLAIN_TEXT" value="18" />
|
||||||
|
<entry key="Python" value="3399" />
|
||||||
</counts>
|
</counts>
|
||||||
</usages-collector>
|
</usages-collector>
|
||||||
</session>
|
</session>
|
||||||
</component>
|
</component>
|
||||||
<component name="FileEditorManager">
|
<component name="FileEditorManager">
|
||||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
<leaf>
|
||||||
<file pinned="false" current-in-tab="true">
|
<file pinned="false" current-in-tab="true">
|
||||||
<entry file="file://$PROJECT_DIR$/readme.md">
|
<entry file="file://$PROJECT_DIR$/config/config.ini">
|
||||||
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state split_layout="SPLIT">
|
<state relative-caret-position="373">
|
||||||
<first_editor relative-caret-position="180">
|
<caret line="44" lean-forward="true" selection-start-line="44" selection-end-line="44" />
|
||||||
<caret line="12" column="31" lean-forward="true" selection-start-line="12" selection-start-column="31" selection-end-line="12" selection-end-column="31" />
|
|
||||||
</first_editor>
|
|
||||||
<second_editor />
|
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
@ -194,22 +236,13 @@
|
|||||||
<option value="SCSS File" />
|
<option value="SCSS File" />
|
||||||
<option value="Setup Script" />
|
<option value="Setup Script" />
|
||||||
<option value="JavaScript File" />
|
<option value="JavaScript File" />
|
||||||
<option value="Python Script" />
|
|
||||||
<option value="HTML File" />
|
<option value="HTML File" />
|
||||||
|
<option value="Python Script" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="FindInProjectRecents">
|
<component name="FindInProjectRecents">
|
||||||
<findStrings>
|
<findStrings>
|
||||||
<find>ConfigParserWithEnv</find>
|
|
||||||
<find>logger</find>
|
|
||||||
<find>app_config</find>
|
|
||||||
<find>dialog_</find>
|
|
||||||
<find>.find</find>
|
|
||||||
<find>.show</find>
|
|
||||||
<find>videos_wrapper</find>
|
|
||||||
<find>videos_loading</find>
|
|
||||||
<find>submit</find>
|
|
||||||
<find>modal_edit_folder</find>
|
<find>modal_edit_folder</find>
|
||||||
<find>modal_update_folder</find>
|
<find>modal_update_folder</find>
|
||||||
<find>modal_delete_folder</find>
|
<find>modal_delete_folder</find>
|
||||||
@ -231,6 +264,15 @@
|
|||||||
<find>settings</find>
|
<find>settings</find>
|
||||||
<find>settings.</find>
|
<find>settings.</find>
|
||||||
<find>manager_delete_after_watched</find>
|
<find>manager_delete_after_watched</find>
|
||||||
|
<find>dist</find>
|
||||||
|
<find>relatedPlaylists</find>
|
||||||
|
<find>thumbnails</find>
|
||||||
|
<find>'thumbnails'</find>
|
||||||
|
<find>thumbn</find>
|
||||||
|
<find>thumbnail</find>
|
||||||
|
<find>get_or_create</find>
|
||||||
|
<find>video</find>
|
||||||
|
<find>class thum</find>
|
||||||
</findStrings>
|
</findStrings>
|
||||||
<replaceStrings>
|
<replaceStrings>
|
||||||
<replace>loading</replace>
|
<replace>loading</replace>
|
||||||
@ -254,8 +296,6 @@
|
|||||||
<component name="IdeDocumentHistory">
|
<component name="IdeDocumentHistory">
|
||||||
<option name="CHANGED_PATHS">
|
<option name="CHANGED_PATHS">
|
||||||
<list>
|
<list>
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_create_modal.html" />
|
|
||||||
<option value="$PROJECT_DIR$/../YoutubeApi-tests/multiinheritance.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_modal.html" />
|
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_modal.html" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_update_modal.html" />
|
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_update_modal.html" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/management/folders.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/management/folders.py" />
|
||||||
@ -277,11 +317,7 @@
|
|||||||
<option value="$PROJECT_DIR$/YtManagerApp/apps.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/apps.py" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/index.js" />
|
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/index.js" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/common.js" />
|
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/common.js" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/scheduler.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/management/videos.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/iterutils.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/utils/iterutils.py" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templatetags/common.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/templatetags/common.py" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templatetags/ratings.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/templatetags/ratings.py" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index_videos.html" />
|
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index_videos.html" />
|
||||||
@ -297,14 +333,20 @@
|
|||||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/customconfigparser.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/utils/customconfigparser.py" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/extended_interpolation_with_env.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/utils/extended_interpolation_with_env.py" />
|
||||||
<option value="$PROJECT_DIR$/config/defaults.ini" />
|
<option value="$PROJECT_DIR$/config/defaults.ini" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/models.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" />
|
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/appconfig.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/appconfig.py" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/management/downloader.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/management/downloader.py" />
|
||||||
<option value="$PROJECT_DIR$/config/config.ini" />
|
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" />
|
||||||
<option value="$PROJECT_DIR$/YtManagerApp/views/index.py" />
|
<option value="$PROJECT_DIR$/YtManagerApp/scheduler.py" />
|
||||||
<option value="$PROJECT_DIR$/readme.md" />
|
<option value="$PROJECT_DIR$/readme.md" />
|
||||||
|
<option value="$PROJECT_DIR$/external/pytaw/README.md" />
|
||||||
|
<option value="$PROJECT_DIR$/external/pytaw/pytaw/youtube.py" />
|
||||||
|
<option value="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" />
|
||||||
|
<option value="$PROJECT_DIR$/YtManagerApp/management/videos.py" />
|
||||||
|
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" />
|
||||||
|
<option value="$PROJECT_DIR$/YtManagerApp/models.py" />
|
||||||
|
<option value="$PROJECT_DIR$/YtManagerApp/views/index.py" />
|
||||||
|
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" />
|
||||||
|
<option value="$PROJECT_DIR$/config/config.ini" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
@ -320,8 +362,8 @@
|
|||||||
</packageJsonPaths>
|
</packageJsonPaths>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectFrameBounds" extendedState="6">
|
<component name="ProjectFrameBounds" extendedState="6">
|
||||||
<option name="x" value="-8" />
|
<option name="x" value="803" />
|
||||||
<option name="y" value="-8" />
|
<option name="y" value="615" />
|
||||||
<option name="width" value="643" />
|
<option name="width" value="643" />
|
||||||
<option name="height" value="321" />
|
<option name="height" value="321" />
|
||||||
</component>
|
</component>
|
||||||
@ -378,6 +420,7 @@
|
|||||||
<foldersAlwaysOnTop value="true" />
|
<foldersAlwaysOnTop value="true" />
|
||||||
</navigator>
|
</navigator>
|
||||||
<panes>
|
<panes>
|
||||||
|
<pane id="Scope" />
|
||||||
<pane id="ProjectPane">
|
<pane id="ProjectPane">
|
||||||
<subPane>
|
<subPane>
|
||||||
<expand>
|
<expand>
|
||||||
@ -385,6 +428,11 @@
|
|||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||||
</path>
|
</path>
|
||||||
|
<path>
|
||||||
|
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||||
|
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||||
|
<item name="config" type="462c0819:PsiDirectoryNode" />
|
||||||
|
</path>
|
||||||
<path>
|
<path>
|
||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||||
@ -394,50 +442,25 @@
|
|||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
<item name="management" type="462c0819:PsiDirectoryNode" />
|
||||||
</path>
|
</path>
|
||||||
<path>
|
<path>
|
||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
<item name="management" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
<item name="jobs" type="462c0819:PsiDirectoryNode" />
|
||||||
</path>
|
</path>
|
||||||
<path>
|
<path>
|
||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
<item name="utils" type="462c0819:PsiDirectoryNode" />
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="import" type="462c0819:PsiDirectoryNode" />
|
|
||||||
</path>
|
|
||||||
<path>
|
|
||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="import" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="bootstrap-4.1.3" type="462c0819:PsiDirectoryNode" />
|
|
||||||
</path>
|
|
||||||
<path>
|
|
||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
|
||||||
</path>
|
|
||||||
<path>
|
|
||||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
|
||||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
|
||||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
|
||||||
</path>
|
</path>
|
||||||
</expand>
|
</expand>
|
||||||
<select />
|
<select />
|
||||||
</subPane>
|
</subPane>
|
||||||
</pane>
|
</pane>
|
||||||
<pane id="Scope" />
|
|
||||||
</panes>
|
</panes>
|
||||||
</component>
|
</component>
|
||||||
<component name="PropertiesComponent">
|
<component name="PropertiesComponent">
|
||||||
@ -445,26 +468,26 @@
|
|||||||
<property name="SearchEverywhereHistoryKey" value="	FILE	file://D:/Dev/youtube-channel-manager/YtManager/settings.py" />
|
<property name="SearchEverywhereHistoryKey" value="	FILE	file://D:/Dev/youtube-channel-manager/YtManager/settings.py" />
|
||||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||||
<property name="database.console.LAST_STATE" value="false" />
|
<property name="database.console.LAST_STATE" value="false" />
|
||||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../YoutubeApi-tests/get_playlist_info.py" />
|
<property name="last_opened_file_path" value="$PROJECT_DIR$/../pytaw" />
|
||||||
<property name="list.type.of.created.stylesheet" value="SCSS" />
|
<property name="list.type.of.created.stylesheet" value="SCSS" />
|
||||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
||||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
||||||
<property name="settings.editor.selected.configurable" value="reference.settings.ide.settings.web.browsers" />
|
<property name="settings.editor.selected.configurable" value="reference.settings.ide.settings.web.browsers" />
|
||||||
</component>
|
</component>
|
||||||
<component name="RecentsManager">
|
<component name="RecentsManager">
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp" />
|
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\management\jobs" />
|
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp\controls" />
|
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\registration" />
|
|
||||||
<recent name="D:\Dev\youtube-channel-manager\config" />
|
|
||||||
</key>
|
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$" />
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp\js" />
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp\js" />
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\views\controls" />
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\views\controls" />
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\views" />
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\views" />
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp" />
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp" />
|
||||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\static\YtManagerApp" />
|
</key>
|
||||||
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/YtManagerApp/import" />
|
||||||
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp" />
|
||||||
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\management\jobs" />
|
||||||
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp\controls" />
|
||||||
|
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\registration" />
|
||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
<component name="RunDashboard">
|
<component name="RunDashboard">
|
||||||
@ -610,29 +633,29 @@
|
|||||||
<frame x="0" y="0" width="1920" height="1048" extended-state="6" />
|
<frame x="0" y="0" width="1920" height="1048" extended-state="6" />
|
||||||
<editor active="true" />
|
<editor active="true" />
|
||||||
<layout>
|
<layout>
|
||||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.18603411" />
|
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.18923241" />
|
||||||
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
|
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
|
||||||
<window_info id="Favorites" order="2" side_tool="true" />
|
<window_info id="Favorites" order="2" side_tool="true" />
|
||||||
<window_info anchor="bottom" id="Message" order="0" />
|
<window_info anchor="bottom" id="Message" order="0" />
|
||||||
<window_info anchor="bottom" id="Find" order="1" sideWeight="0.49733475" weight="0.329718" />
|
<window_info anchor="bottom" id="Find" order="1" sideWeight="0.49520257" weight="0.38152173" />
|
||||||
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.5245203" visible="true" weight="0.33913043" />
|
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.5234541" visible="true" weight="0.2228261" />
|
||||||
<window_info anchor="bottom" id="Cvs" order="3" weight="0.25" />
|
<window_info anchor="bottom" id="Cvs" order="3" weight="0.25" />
|
||||||
<window_info anchor="bottom" id="Inspection" order="4" weight="0.4" />
|
<window_info anchor="bottom" id="Inspection" order="4" weight="0.4" />
|
||||||
<window_info anchor="bottom" id="Debug" order="5" sideWeight="0.49520257" weight="0.37608695" />
|
<window_info anchor="bottom" id="Debug" order="5" sideWeight="0.4936034" weight="0.37608695" />
|
||||||
<window_info anchor="bottom" id="TODO" order="6" weight="0.329718" />
|
<window_info anchor="bottom" id="TODO" order="6" weight="0.329718" />
|
||||||
<window_info anchor="bottom" id="manage.py@youtube-channel-manager" order="7" sideWeight="0.49626866" weight="0.49347827" />
|
<window_info anchor="bottom" id="manage.py@youtube-channel-manager" order="7" sideWeight="0.49573562" weight="0.49347827" />
|
||||||
<window_info anchor="bottom" id="Docker" order="8" show_stripe_button="false" />
|
<window_info anchor="bottom" id="Docker" order="8" show_stripe_button="false" />
|
||||||
<window_info anchor="bottom" id="Database Changes" order="9" weight="0.3285968" />
|
<window_info anchor="bottom" id="Database Changes" order="9" weight="0.3285968" />
|
||||||
<window_info anchor="bottom" id="Event Log" order="10" sideWeight="0.47547975" side_tool="true" visible="true" weight="0.33913043" />
|
<window_info anchor="bottom" id="Event Log" order="10" sideWeight="0.47654584" side_tool="true" visible="true" weight="0.2228261" />
|
||||||
<window_info anchor="bottom" id="Version Control" order="11" sideWeight="0.49946696" weight="0.329718" />
|
<window_info anchor="bottom" id="Version Control" order="11" sideWeight="0.49946696" weight="0.329718" />
|
||||||
<window_info anchor="bottom" id="Terminal" order="12" sideWeight="0.49626866" weight="0.3576087" />
|
<window_info anchor="bottom" id="Terminal" order="12" sideWeight="0.49573562" weight="0.3576087" />
|
||||||
<window_info anchor="bottom" id="Python Console" order="13" sideWeight="0.49733475" weight="0.3347826" />
|
<window_info anchor="bottom" id="Python Console" order="13" sideWeight="0.4968017" weight="0.33804348" />
|
||||||
<window_info anchor="bottom" id="Database Console" order="14" sideWeight="0.49838188" weight="0.21908894" />
|
<window_info anchor="bottom" id="Database Console" order="14" sideWeight="0.49838188" weight="0.21908894" />
|
||||||
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
|
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
|
||||||
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
|
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
|
||||||
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
|
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
|
||||||
<window_info anchor="right" id="SciView" order="3" weight="0.32995737" />
|
<window_info anchor="right" id="SciView" order="3" weight="0.32995737" />
|
||||||
<window_info anchor="right" id="Database" order="4" weight="0.2675906" />
|
<window_info anchor="right" id="Database" order="4" visible="true" weight="0.20469083" />
|
||||||
</layout>
|
</layout>
|
||||||
<layout-to-restore>
|
<layout-to-restore>
|
||||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.15138593" />
|
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.15138593" />
|
||||||
@ -667,75 +690,6 @@
|
|||||||
<option name="myLimit" value="2678400000" />
|
<option name="myLimit" value="2678400000" />
|
||||||
</component>
|
</component>
|
||||||
<component name="editorHistoryManager">
|
<component name="editorHistoryManager">
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/utils/youtube.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="187">
|
|
||||||
<caret line="11" lean-forward="true" selection-start-line="11" selection-end-line="11" />
|
|
||||||
<folding>
|
|
||||||
<element signature="e#0#43#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/videos.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="357">
|
|
||||||
<caret line="24" column="9" lean-forward="true" selection-start-line="24" selection-start-column="9" selection-end-line="24" selection-end-column="9" />
|
|
||||||
<folding>
|
|
||||||
<element signature="e#0#71#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="68">
|
|
||||||
<caret line="4" selection-start-line="4" selection-end-line="4" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templatetags/common.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="459">
|
|
||||||
<caret line="30" lean-forward="true" selection-start-line="30" selection-end-line="30" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templatetags/ratings.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="187">
|
|
||||||
<caret line="30" column="9" lean-forward="true" selection-start-line="30" selection-start-column="9" selection-end-line="30" selection-end-column="9" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views/old_views.py" />
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state>
|
|
||||||
<caret selection-end-column="48" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/template/base.py" />
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index_videos.html">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="143">
|
|
||||||
<caret line="23" column="56" selection-start-line="23" selection-start-column="56" selection-end-line="23" selection-end-column="56" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/core/handlers/exception.py" />
|
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/views/generic/base.py" />
|
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/views/generic/edit.py" />
|
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/query.py" />
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/server_settings.html" />
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/settings.html">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="187">
|
|
||||||
<caret line="11" column="18" lean-forward="true" selection-start-line="11" selection-start-column="18" selection-end-line="11" selection-end-column="18" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.scss">
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.scss">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="391">
|
<state relative-caret-position="391">
|
||||||
@ -751,20 +705,6 @@
|
|||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_video">
|
|
||||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
|
||||||
<state>
|
|
||||||
<filtering enabled="true" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_subscription">
|
|
||||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
|
||||||
<state>
|
|
||||||
<filtering enabled="true" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/fields/__init__.py" />
|
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/fields/__init__.py" />
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/forms/models.py" />
|
<entry file="file://C:/Python36/Lib/site-packages/django/forms/models.py" />
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/contrib/auth/decorators.py" />
|
<entry file="file://C:/Python36/Lib/site-packages/django/contrib/auth/decorators.py" />
|
||||||
@ -772,9 +712,6 @@
|
|||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="136">
|
<state relative-caret-position="136">
|
||||||
<caret line="8" lean-forward="true" selection-start-line="8" selection-end-line="8" />
|
<caret line="8" lean-forward="true" selection-start-line="8" selection-end-line="8" />
|
||||||
<folding>
|
|
||||||
<element signature="e#0#42#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
@ -797,48 +734,15 @@
|
|||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="459">
|
<state relative-caret-position="459">
|
||||||
<caret line="92" lean-forward="true" selection-start-line="92" selection-end-line="92" />
|
<caret line="92" lean-forward="true" selection-start-line="92" selection-end-line="92" />
|
||||||
<folding>
|
|
||||||
<element signature="e#0#9#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
<entry file="file://C:/Python36/Lib/collections/__init__.py" />
|
<entry file="file://C:/Python36/Lib/collections/__init__.py" />
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="510">
|
|
||||||
<caret line="102" column="12" lean-forward="true" selection-start-line="102" selection-start-column="12" selection-end-line="102" selection-end-column="12" />
|
|
||||||
<folding>
|
|
||||||
<element signature="e#0#37#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/scheduler.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="136">
|
|
||||||
<caret line="8" column="32" lean-forward="true" selection-start-line="8" selection-start-column="32" selection-end-line="8" selection-end-column="32" />
|
|
||||||
<folding>
|
|
||||||
<element signature="e#0#14#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$USER_HOME$/.PyCharm2018.2/system/python_stubs/-1184660488/builtins.py" />
|
<entry file="file://$USER_HOME$/.PyCharm2018.2/system/python_stubs/-1184660488/builtins.py" />
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/urls.py">
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/urls.py">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="408">
|
<state relative-caret-position="408">
|
||||||
<caret line="33" selection-start-line="33" selection-end-line="33" />
|
<caret line="33" selection-start-line="33" selection-end-line="33" />
|
||||||
<folding>
|
|
||||||
<element signature="e#643#675#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManager/settings.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="746">
|
|
||||||
<caret line="132" selection-start-line="132" selection-end-line="132" />
|
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
@ -856,29 +760,13 @@
|
|||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="187">
|
<state relative-caret-position="187">
|
||||||
<caret line="11" selection-start-line="11" selection-end-line="11" />
|
<caret line="11" selection-start-line="11" selection-end-line="11" />
|
||||||
<folding>
|
|
||||||
<element signature="e#0#42#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
<entry file="file://C:/Python36/Lib/configparser.py" />
|
<entry file="file://C:/Python36/Lib/configparser.py" />
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/apscheduler/executors/base.py" />
|
<entry file="file://C:/Python36/Lib/site-packages/apscheduler/executors/base.py" />
|
||||||
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/fields/related_descriptors.py" />
|
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/fields/related_descriptors.py" />
|
||||||
<entry file="file:///usr/lib/python3/dist-packages/django/db/models/base.py">
|
<entry file="file:///usr/lib/python3/dist-packages/django/db/models/base.py" />
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="193">
|
|
||||||
<caret line="1652" selection-start-line="1652" selection-end-line="1652" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views/index.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state>
|
|
||||||
<caret column="49" selection-start-column="49" selection-end-column="49" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/appmain.py">
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/appmain.py">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="165">
|
<state relative-caret-position="165">
|
||||||
@ -916,30 +804,6 @@
|
|||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="311">
|
|
||||||
<caret line="123" selection-start-line="123" selection-end-line="123" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/config/config.ini">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="491">
|
|
||||||
<caret line="59" selection-start-line="59" selection-end-line="59" />
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/models.py">
|
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
|
||||||
<state relative-caret-position="345">
|
|
||||||
<caret line="23" column="30" lean-forward="true" selection-start-line="23" selection-start-column="30" selection-end-line="23" selection-end-column="30" />
|
|
||||||
<folding>
|
|
||||||
<element signature="e#0#14#0" expanded="true" />
|
|
||||||
</folding>
|
|
||||||
</state>
|
|
||||||
</provider>
|
|
||||||
</entry>
|
|
||||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.gitignore">
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.gitignore">
|
||||||
<provider selected="true" editor-type-id="text-editor">
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
<state relative-caret-position="45">
|
<state relative-caret-position="45">
|
||||||
@ -954,15 +818,204 @@
|
|||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="485">
|
||||||
|
<caret line="37" column="65" selection-start-line="37" selection-start-column="65" selection-end-line="38" selection-end-column="76" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#14#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file:///usr/local/lib/python3.6/dist-packages/apscheduler/schedulers/base.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="421">
|
||||||
|
<caret line="440" column="16" selection-start-line="440" selection-start-column="16" selection-end-line="440" selection-end-column="16" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
<entry file="file://$PROJECT_DIR$/readme.md">
|
<entry file="file://$PROJECT_DIR$/readme.md">
|
||||||
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
||||||
<state split_layout="SPLIT">
|
<state split_layout="SPLIT">
|
||||||
<first_editor relative-caret-position="180">
|
<first_editor relative-caret-position="60">
|
||||||
<caret line="12" column="31" lean-forward="true" selection-start-line="12" selection-start-column="31" selection-end-line="12" selection-end-column="31" />
|
<caret line="4" selection-start-line="4" selection-end-line="4" />
|
||||||
</first_editor>
|
</first_editor>
|
||||||
<second_editor />
|
<second_editor />
|
||||||
</state>
|
</state>
|
||||||
</provider>
|
</provider>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManager/settings.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="1046">
|
||||||
|
<caret line="132" selection-start-line="132" selection-end-line="132" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_subscription">
|
||||||
|
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||||
|
<state>
|
||||||
|
<filtering enabled="true" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/auth_group">
|
||||||
|
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||||
|
<state>
|
||||||
|
<filtering enabled="true" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_video">
|
||||||
|
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||||
|
<state>
|
||||||
|
<filtering enabled="true" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/external/pytaw/docs/conf.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor" />
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/scheduler.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="300">
|
||||||
|
<caret line="20" column="5" selection-start-line="20" selection-start-column="5" selection-end-line="20" selection-end-column="5" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/utils/iterutils.py" />
|
||||||
|
<entry file="das://77df9da5-0b97-445e-a895-744ef8257a74/schema/main/table/ytmanagerapp_channel">
|
||||||
|
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||||
|
<state>
|
||||||
|
<filtering enabled="true" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="das://77df9da5-0b97-445e-a895-744ef8257a74/schema/main/table/ytmanagerapp_subscription">
|
||||||
|
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||||
|
<state>
|
||||||
|
<filtering enabled="true" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="das://77df9da5-0b97-445e-a895-744ef8257a74/schema/main/table/ytmanagerapp_video">
|
||||||
|
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||||
|
<state>
|
||||||
|
<filtering enabled="true" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/external/pytaw/README.md">
|
||||||
|
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
||||||
|
<state split_layout="SPLIT">
|
||||||
|
<first_editor relative-caret-position="75">
|
||||||
|
<caret line="5" lean-forward="true" selection-start-line="5" selection-end-line="5" />
|
||||||
|
</first_editor>
|
||||||
|
<second_editor />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/management.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="1500">
|
||||||
|
<caret line="100" column="20" lean-forward="true" selection-start-line="100" selection-start-column="20" selection-end-line="100" selection-end-column="20" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#65#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/videos.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="-308">
|
||||||
|
<caret line="8" lean-forward="true" selection-start-line="8" selection-end-line="8" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#9#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/external/pytaw/pytaw/youtube.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="187">
|
||||||
|
<caret line="174" column="11" lean-forward="true" selection-start-line="174" selection-start-column="11" selection-end-line="174" selection-end-column="11" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#18#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file:///usr/local/lib/python3.6/dist-packages/django/forms/utils.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="75">
|
||||||
|
<caret line="5" selection-start-line="5" selection-end-line="5" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file:///usr/local/lib/python3.6/dist-packages/django/db/backends/sqlite3/base.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="138">
|
||||||
|
<caret line="314" column="31" selection-start-line="314" selection-start-column="31" selection-end-line="314" selection-end-column="31" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/models.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="251">
|
||||||
|
<caret line="280" column="32" selection-start-line="280" selection-start-column="22" selection-end-line="280" selection-end-column="32" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#14#0" expanded="true" />
|
||||||
|
<marker date="1540831325280" expanded="true" signature="7713:7719" ph="..." />
|
||||||
|
<marker date="1540831325280" expanded="true" signature="13276:13861" ph="..." />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/utils/youtube.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="120">
|
||||||
|
<caret line="8" column="27" selection-start-line="8" selection-start-column="27" selection-end-line="8" selection-end-column="27" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#32#0" expanded="true" />
|
||||||
|
<marker date="1540827548686" expanded="true" signature="184:185" ph="..." />
|
||||||
|
<marker date="1540827548686" expanded="true" signature="761:766" ph="..." />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="270">
|
||||||
|
<caret line="18" column="71" selection-start-line="18" selection-start-column="71" selection-end-line="18" selection-end-column="71" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#12#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/views/index.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="521">
|
||||||
|
<caret line="299" selection-start-line="299" selection-end-line="299" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="408">
|
||||||
|
<caret line="99" column="49" selection-start-line="99" selection-start-column="49" selection-end-line="99" selection-end-column="49" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#0#37#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/config/config.ini">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="373">
|
||||||
|
<caret line="44" lean-forward="true" selection-start-line="44" selection-end-line="44" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -35,4 +35,5 @@ def schedule_delete_video(video: Video):
|
|||||||
:param video:
|
:param video:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
scheduler.instance.add_job(delete_video, args=[video])
|
job = scheduler.scheduler.add_job(delete_video, args=[video])
|
||||||
|
log.info('Scheduled delete video job video=(%s), job=%s', video, job.id)
|
||||||
|
@ -25,8 +25,8 @@ def __get_valid_path(path):
|
|||||||
def __build_youtube_dl_params(video: Video):
|
def __build_youtube_dl_params(video: Video):
|
||||||
# resolve path
|
# resolve path
|
||||||
pattern_dict = {
|
pattern_dict = {
|
||||||
'channel': video.subscription.channel.name,
|
'channel': video.subscription.channel_name,
|
||||||
'channel_id': video.subscription.channel.channel_id,
|
'channel_id': video.subscription.channel_id,
|
||||||
'playlist': video.subscription.name,
|
'playlist': video.subscription.name,
|
||||||
'playlist_id': video.subscription.playlist_id,
|
'playlist_id': video.subscription.playlist_id,
|
||||||
'playlist_index': "{:03d}".format(1 + video.playlist_index),
|
'playlist_index': "{:03d}".format(1 + video.playlist_index),
|
||||||
@ -88,7 +88,7 @@ def download_video(video: Video, attempt: int = 1):
|
|||||||
|
|
||||||
elif attempt <= max_attempts:
|
elif attempt <= max_attempts:
|
||||||
log.warning('Re-enqueueing video (attempt %d/%d)', attempt, max_attempts)
|
log.warning('Re-enqueueing video (attempt %d/%d)', attempt, max_attempts)
|
||||||
scheduler.instance.add_job(download_video, args=[video, attempt + 1])
|
__schedule_download_video(video, attempt + 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
log.error('Multiple attempts to download video %d [%s %s] failed!', video.id, video.video_id, video.name)
|
log.error('Multiple attempts to download video %d [%s %s] failed!', video.id, video.video_id, video.name)
|
||||||
@ -96,10 +96,15 @@ def download_video(video: Video, attempt: int = 1):
|
|||||||
video.save()
|
video.save()
|
||||||
|
|
||||||
|
|
||||||
|
def __schedule_download_video(video: Video, attempt=1):
|
||||||
|
job = scheduler.scheduler.add_job(download_video, args=[video, attempt])
|
||||||
|
log.info('Scheduled download video job video=(%s), attempt=%d, job=%s', video, attempt, job.id)
|
||||||
|
|
||||||
|
|
||||||
def schedule_download_video(video: Video):
|
def schedule_download_video(video: Video):
|
||||||
"""
|
"""
|
||||||
Schedules a download video job to run immediately.
|
Schedules a download video job to run immediately.
|
||||||
:param video:
|
:param video:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
scheduler.instance.add_job(download_video, args=[video, 1])
|
__schedule_download_video(video)
|
||||||
|
@ -7,9 +7,8 @@ from apscheduler.triggers.cron import CronTrigger
|
|||||||
from YtManagerApp import scheduler
|
from YtManagerApp import scheduler
|
||||||
from YtManagerApp.appconfig import settings
|
from YtManagerApp.appconfig import settings
|
||||||
from YtManagerApp.management.downloader import fetch_thumbnail, downloader_process_all, downloader_process_subscription
|
from YtManagerApp.management.downloader import fetch_thumbnail, downloader_process_all, downloader_process_subscription
|
||||||
from YtManagerApp.management.videos import create_video
|
|
||||||
from YtManagerApp.models import *
|
from YtManagerApp.models import *
|
||||||
from YtManagerApp.utils.youtube import YoutubeAPI
|
from YtManagerApp.utils import youtube
|
||||||
|
|
||||||
log = logging.getLogger('sync')
|
log = logging.getLogger('sync')
|
||||||
__lock = Lock()
|
__lock = Lock()
|
||||||
@ -17,23 +16,25 @@ __lock = Lock()
|
|||||||
_ENABLE_UPDATE_STATS = False
|
_ENABLE_UPDATE_STATS = False
|
||||||
|
|
||||||
|
|
||||||
def __check_new_videos_sub(subscription: Subscription, yt_api: YoutubeAPI):
|
def __check_new_videos_sub(subscription: Subscription, yt_api: youtube.YoutubeAPI):
|
||||||
# Get list of videos
|
# Get list of videos
|
||||||
for video in yt_api.list_playlist_videos(subscription.playlist_id):
|
for item in yt_api.playlist_items(subscription.playlist_id):
|
||||||
results = Video.objects.filter(video_id=video.getVideoId(), subscription=subscription)
|
results = Video.objects.filter(video_id=item.resource_video_id, subscription=subscription)
|
||||||
if len(results) == 0:
|
if len(results) == 0:
|
||||||
log.info('New video for subscription %s: %s %s"', subscription, video.getVideoId(), video.getTitle())
|
log.info('New video for subscription %s: %s %s"', subscription, item.resource_video_id, item.title)
|
||||||
db_video = create_video(video, subscription)
|
Video.create(item, subscription)
|
||||||
else:
|
|
||||||
if not _ENABLE_UPDATE_STATS:
|
|
||||||
continue
|
|
||||||
db_video = results.first()
|
|
||||||
|
|
||||||
# Update video stats - rating and view count
|
if _ENABLE_UPDATE_STATS:
|
||||||
stats = yt_api.get_single_video_stats(db_video.video_id)
|
all_vids = Video.objects.filter(subscription=subscription)
|
||||||
db_video.rating = stats.get_like_count() / (stats.get_like_count() + stats.get_dislike_count())
|
all_vids_ids = [video.video_id for video in all_vids]
|
||||||
db_video.views = stats.get_view_count()
|
all_vids_dict = {v.video_id: v for v in all_vids}
|
||||||
db_video.save()
|
|
||||||
|
for yt_video in yt_api.videos(all_vids_ids, part='id,statistics'):
|
||||||
|
video = all_vids_dict.get(yt_video.id)
|
||||||
|
if yt_video.like_count is not None and yt_video.dislike_count is not None:
|
||||||
|
video.rating = yt_video.n_likes / (yt_video.n_likes + yt_video.n_dislikes)
|
||||||
|
video.views = yt_video.n_views
|
||||||
|
video.save()
|
||||||
|
|
||||||
|
|
||||||
def __detect_deleted(subscription: Subscription):
|
def __detect_deleted(subscription: Subscription):
|
||||||
@ -82,11 +83,6 @@ def __fetch_thumbnails_obj(iterable, obj_type, id_attr):
|
|||||||
|
|
||||||
|
|
||||||
def __fetch_thumbnails():
|
def __fetch_thumbnails():
|
||||||
# Fetch thumbnails
|
|
||||||
log.info("Fetching channel thumbnails... ")
|
|
||||||
__fetch_thumbnails_obj(Channel.objects.filter(icon_default__istartswith='http'), 'channel', 'channel_id')
|
|
||||||
__fetch_thumbnails_obj(Channel.objects.filter(icon_best__istartswith='http'), 'channel', 'channel_id')
|
|
||||||
|
|
||||||
log.info("Fetching subscription thumbnails... ")
|
log.info("Fetching subscription thumbnails... ")
|
||||||
__fetch_thumbnails_obj(Subscription.objects.filter(icon_default__istartswith='http'), 'sub', 'playlist_id')
|
__fetch_thumbnails_obj(Subscription.objects.filter(icon_default__istartswith='http'), 'sub', 'playlist_id')
|
||||||
__fetch_thumbnails_obj(Subscription.objects.filter(icon_best__istartswith='http'), 'sub', 'playlist_id')
|
__fetch_thumbnails_obj(Subscription.objects.filter(icon_best__istartswith='http'), 'sub', 'playlist_id')
|
||||||
@ -107,7 +103,7 @@ def synchronize():
|
|||||||
|
|
||||||
# Sync subscribed playlists/channels
|
# Sync subscribed playlists/channels
|
||||||
log.info("Sync - checking videos")
|
log.info("Sync - checking videos")
|
||||||
yt_api = YoutubeAPI.build_public()
|
yt_api = youtube.YoutubeAPI.build_public()
|
||||||
for subscription in Subscription.objects.all():
|
for subscription in Subscription.objects.all():
|
||||||
__check_new_videos_sub(subscription, yt_api)
|
__check_new_videos_sub(subscription, yt_api)
|
||||||
__detect_deleted(subscription)
|
__detect_deleted(subscription)
|
||||||
@ -128,7 +124,7 @@ def synchronize_subscription(subscription: Subscription):
|
|||||||
__lock.acquire()
|
__lock.acquire()
|
||||||
try:
|
try:
|
||||||
log.info("Running synchronization for single subscription %d [%s]", subscription.id, subscription.name)
|
log.info("Running synchronization for single subscription %d [%s]", subscription.id, subscription.name)
|
||||||
yt_api = YoutubeAPI.build_public()
|
yt_api = youtube.YoutubeAPI.build_public()
|
||||||
|
|
||||||
log.info("Sync - checking videos")
|
log.info("Sync - checking videos")
|
||||||
__check_new_videos_sub(subscription, yt_api)
|
__check_new_videos_sub(subscription, yt_api)
|
||||||
@ -148,12 +144,15 @@ def synchronize_subscription(subscription: Subscription):
|
|||||||
|
|
||||||
def schedule_synchronize_global():
|
def schedule_synchronize_global():
|
||||||
trigger = CronTrigger.from_crontab(settings.get('global', 'SynchronizationSchedule'))
|
trigger = CronTrigger.from_crontab(settings.get('global', 'SynchronizationSchedule'))
|
||||||
scheduler.instance.add_job(synchronize, trigger, max_instances=1, coalesce=True)
|
job = scheduler.scheduler.add_job(synchronize, trigger, max_instances=1, coalesce=True)
|
||||||
|
log.info('Scheduled synchronize job job=%s', job.id)
|
||||||
|
|
||||||
|
|
||||||
def schedule_synchronize_now():
|
def schedule_synchronize_now():
|
||||||
scheduler.instance.add_job(synchronize, max_instances=1, coalesce=True)
|
job = scheduler.scheduler.add_job(synchronize, max_instances=1, coalesce=True)
|
||||||
|
log.info('Scheduled synchronize now job job=%s', job.id)
|
||||||
|
|
||||||
|
|
||||||
def schedule_synchronize_now_subscription(subscription: Subscription):
|
def schedule_synchronize_now_subscription(subscription: Subscription):
|
||||||
scheduler.instance.add_job(synchronize_subscription, args=[subscription])
|
job = scheduler.scheduler.add_job(synchronize_subscription, args=[subscription])
|
||||||
|
log.info('Scheduled synchronize subscription job subscription=(%s), job=%s', subscription, job.id)
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
|
||||||
|
|
||||||
from YtManagerApp.models import SubscriptionFolder, Subscription, Video, Channel
|
|
||||||
from YtManagerApp.utils.youtube import YoutubeAPI, YoutubeChannelInfo
|
|
||||||
|
|
||||||
|
|
||||||
class FolderManager(object):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_or_edit(fid, name, parent_id):
|
|
||||||
# Create or edit
|
|
||||||
if fid == '#':
|
|
||||||
folder = SubscriptionFolder()
|
|
||||||
else:
|
|
||||||
folder = SubscriptionFolder.objects.get(id=int(fid))
|
|
||||||
|
|
||||||
# Set attributes
|
|
||||||
folder.name = name
|
|
||||||
if parent_id == '#':
|
|
||||||
folder.parent = None
|
|
||||||
else:
|
|
||||||
folder.parent = SubscriptionFolder.objects.get(id=int(parent_id))
|
|
||||||
|
|
||||||
FolderManager.__validate(folder)
|
|
||||||
folder.save()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __validate(folder: SubscriptionFolder):
|
|
||||||
# Make sure folder name is unique in the parent folder
|
|
||||||
for dbFolder in SubscriptionFolder.objects.filter(parent_id=folder.parent_id):
|
|
||||||
if dbFolder.id != folder.id and dbFolder.name == folder.name:
|
|
||||||
raise ValueError('Folder name is not unique!')
|
|
||||||
|
|
||||||
# Prevent parenting loops
|
|
||||||
current = folder
|
|
||||||
visited = []
|
|
||||||
|
|
||||||
while not (current is None):
|
|
||||||
if current in visited:
|
|
||||||
raise ValueError('Parenting cycle detected!')
|
|
||||||
visited.append(current)
|
|
||||||
current = current.parent
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete(fid: int):
|
|
||||||
folder = SubscriptionFolder.objects.get(id=fid)
|
|
||||||
folder.delete()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def list_videos(fid: int):
|
|
||||||
folder = SubscriptionFolder.objects.get(id=fid)
|
|
||||||
folder_list = []
|
|
||||||
queue = [folder]
|
|
||||||
while len(queue) > 0:
|
|
||||||
folder = queue.pop()
|
|
||||||
folder_list.append(folder)
|
|
||||||
queue.extend(SubscriptionFolder.objects.filter(parent=folder))
|
|
||||||
|
|
||||||
return Video.objects.filter(subscription__parent_folder__in=folder_list).order_by('-publish_date')
|
|
||||||
|
|
||||||
|
|
||||||
class SubscriptionManager(object):
|
|
||||||
__scheduler = BackgroundScheduler()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_or_edit(sid, url, name, parent_id):
|
|
||||||
# Create or edit
|
|
||||||
if sid == '#':
|
|
||||||
SubscriptionManager.create(url, parent_id, YoutubeAPI.build_public())
|
|
||||||
else:
|
|
||||||
sub = Subscription.objects.get(id=int(sid))
|
|
||||||
sub.name = name
|
|
||||||
|
|
||||||
if parent_id == '#':
|
|
||||||
sub.parent_folder = None
|
|
||||||
else:
|
|
||||||
sub.parent_folder = SubscriptionFolder.objects.get(id=int(parent_id))
|
|
||||||
|
|
||||||
sub.save()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create(url, parent_id, yt_api: YoutubeAPI):
|
|
||||||
sub = Subscription()
|
|
||||||
# Set parent
|
|
||||||
if parent_id == '#':
|
|
||||||
sub.parent_folder = None
|
|
||||||
else:
|
|
||||||
sub.parent_folder = SubscriptionFolder.objects.get(id=int(parent_id))
|
|
||||||
|
|
||||||
# Pull information about the channel and playlist
|
|
||||||
url_type, url_id = yt_api.parse_channel_url(url)
|
|
||||||
|
|
||||||
if url_type == 'playlist_id':
|
|
||||||
info_playlist = yt_api.get_playlist_info(url_id)
|
|
||||||
channel = SubscriptionManager.__get_or_create_channel('channel_id', info_playlist.getChannelId(), yt_api)
|
|
||||||
sub.name = info_playlist.getTitle()
|
|
||||||
sub.playlist_id = info_playlist.getId()
|
|
||||||
sub.description = info_playlist.getDescription()
|
|
||||||
sub.channel = channel
|
|
||||||
sub.icon_default = info_playlist.getDefaultThumbnailUrl()
|
|
||||||
sub.icon_best = info_playlist.getBestThumbnailUrl()
|
|
||||||
|
|
||||||
else:
|
|
||||||
channel = SubscriptionManager.__get_or_create_channel(url_type, url_id, yt_api)
|
|
||||||
# No point in getting the 'uploads' playlist info
|
|
||||||
sub.name = channel.name
|
|
||||||
sub.playlist_id = channel.upload_playlist_id
|
|
||||||
sub.description = channel.description
|
|
||||||
sub.channel = channel
|
|
||||||
sub.icon_default = channel.icon_default
|
|
||||||
sub.icon_best = channel.icon_best
|
|
||||||
|
|
||||||
sub.save()
|
|
||||||
|
|
@ -1,25 +1,10 @@
|
|||||||
from YtManagerApp.models import Subscription, Video, SubscriptionFolder
|
|
||||||
from YtManagerApp.utils.youtube import YoutubePlaylistItem
|
|
||||||
from typing import Optional
|
|
||||||
import re
|
import re
|
||||||
from django.db.models import Q
|
from typing import Optional
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from YtManagerApp.models import Subscription, Video, SubscriptionFolder
|
||||||
def create_video(yt_video: YoutubePlaylistItem, subscription: Subscription):
|
|
||||||
video = Video()
|
|
||||||
video.video_id = yt_video.getVideoId()
|
|
||||||
video.name = yt_video.getTitle()
|
|
||||||
video.description = yt_video.getDescription()
|
|
||||||
video.watched = False
|
|
||||||
video.downloaded_path = None
|
|
||||||
video.subscription = subscription
|
|
||||||
video.playlist_index = yt_video.getPlaylistIndex()
|
|
||||||
video.publish_date = yt_video.getPublishDate()
|
|
||||||
video.icon_default = yt_video.getDefaultThumbnailUrl()
|
|
||||||
video.icon_best = yt_video.getBestThumbnailUrl()
|
|
||||||
video.save()
|
|
||||||
return video
|
|
||||||
|
|
||||||
|
|
||||||
def get_videos(user: User,
|
def get_videos(user: User,
|
||||||
|
32
YtManagerApp/migrations/0007_auto_20181029_1638.py
Normal file
32
YtManagerApp/migrations/0007_auto_20181029_1638.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 2.1.2 on 2018-10-29 16:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('YtManagerApp', '0006_auto_20181027_0256'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='subscription',
|
||||||
|
name='channel',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subscription',
|
||||||
|
name='channel_id',
|
||||||
|
field=models.CharField(default='test', max_length=128),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subscription',
|
||||||
|
name='channel_name',
|
||||||
|
field=models.CharField(default='Unknown', max_length=1024),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Channel',
|
||||||
|
),
|
||||||
|
]
|
@ -6,7 +6,7 @@ from django.contrib.auth.models import User
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
from YtManagerApp.utils.youtube import YoutubeAPI, YoutubeChannelInfo, YoutubePlaylistInfo
|
from YtManagerApp.utils import youtube
|
||||||
|
|
||||||
# help_text = user shown text
|
# help_text = user shown text
|
||||||
# verbose_name = user shown name
|
# verbose_name = user shown name
|
||||||
@ -176,6 +176,9 @@ class SubscriptionFolder(models.Model):
|
|||||||
current = current.parent
|
current = current.parent
|
||||||
return s[:-3]
|
return s[:-3]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'folder {self.id}, name="{self.name}"'
|
||||||
|
|
||||||
def delete_folder(self, keep_subscriptions: bool):
|
def delete_folder(self, keep_subscriptions: bool):
|
||||||
if keep_subscriptions:
|
if keep_subscriptions:
|
||||||
|
|
||||||
@ -225,92 +228,13 @@ class SubscriptionFolder(models.Model):
|
|||||||
return data_collected
|
return data_collected
|
||||||
|
|
||||||
|
|
||||||
class Channel(models.Model):
|
|
||||||
channel_id = models.TextField(null=False, unique=True)
|
|
||||||
username = models.TextField(null=True, unique=True)
|
|
||||||
custom_url = models.TextField(null=True, unique=True)
|
|
||||||
name = models.TextField()
|
|
||||||
description = models.TextField()
|
|
||||||
icon_default = models.TextField()
|
|
||||||
icon_best = models.TextField()
|
|
||||||
upload_playlist_id = models.TextField()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_by_channel_id(channel_id):
|
|
||||||
result = Channel.objects.filter(channel_id=channel_id)
|
|
||||||
if len(result) > 0:
|
|
||||||
return result.first()
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_by_username(username):
|
|
||||||
result = Channel.objects.filter(username=username)
|
|
||||||
if len(result) > 0:
|
|
||||||
return result.first()
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_by_custom_url(custom_url):
|
|
||||||
result = Channel.objects.filter(custom_url=custom_url)
|
|
||||||
if len(result) > 0:
|
|
||||||
return result.first()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def fill(self, yt_channel_info: YoutubeChannelInfo):
|
|
||||||
self.channel_id = yt_channel_info.getId()
|
|
||||||
self.custom_url = yt_channel_info.getCustomUrl()
|
|
||||||
self.name = yt_channel_info.getTitle()
|
|
||||||
self.description = yt_channel_info.getDescription()
|
|
||||||
self.icon_default = yt_channel_info.getDefaultThumbnailUrl()
|
|
||||||
self.icon_best = yt_channel_info.getBestThumbnailUrl()
|
|
||||||
self.upload_playlist_id = yt_channel_info.getUploadsPlaylist()
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_or_create(url_type: str, url_id: str, yt_api: YoutubeAPI):
|
|
||||||
channel: Channel = None
|
|
||||||
info_channel: YoutubeChannelInfo = None
|
|
||||||
|
|
||||||
if url_type == 'user':
|
|
||||||
channel = Channel.find_by_username(url_id)
|
|
||||||
if not channel:
|
|
||||||
info_channel = yt_api.get_channel_info_by_username(url_id)
|
|
||||||
channel = Channel.find_by_channel_id(info_channel.getId())
|
|
||||||
|
|
||||||
elif url_type == 'channel_id':
|
|
||||||
channel = Channel.find_by_channel_id(url_id)
|
|
||||||
if not channel:
|
|
||||||
info_channel = yt_api.get_channel_info(url_id)
|
|
||||||
|
|
||||||
elif url_type == 'channel_custom':
|
|
||||||
channel = Channel.find_by_custom_url(url_id)
|
|
||||||
if not channel:
|
|
||||||
found_channel_id = yt_api.search_channel(url_id)
|
|
||||||
channel = Channel.find_by_channel_id(found_channel_id)
|
|
||||||
if not channel:
|
|
||||||
info_channel = yt_api.get_channel_info(found_channel_id)
|
|
||||||
|
|
||||||
# If we downloaded information about the channel, store information
|
|
||||||
# about the channel here.
|
|
||||||
if info_channel:
|
|
||||||
if not channel:
|
|
||||||
channel = Channel()
|
|
||||||
if url_type == 'user':
|
|
||||||
channel.username = url_id
|
|
||||||
channel.fill(info_channel)
|
|
||||||
|
|
||||||
return channel
|
|
||||||
|
|
||||||
|
|
||||||
class Subscription(models.Model):
|
class Subscription(models.Model):
|
||||||
name = models.CharField(null=False, max_length=1024)
|
name = models.CharField(null=False, max_length=1024)
|
||||||
parent_folder = models.ForeignKey(SubscriptionFolder, on_delete=models.CASCADE, null=True, blank=True)
|
parent_folder = models.ForeignKey(SubscriptionFolder, on_delete=models.CASCADE, null=True, blank=True)
|
||||||
playlist_id = models.CharField(null=False, max_length=128)
|
playlist_id = models.CharField(null=False, max_length=128)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
channel = models.ForeignKey(Channel, on_delete=models.CASCADE)
|
channel_id = models.CharField(max_length=128)
|
||||||
|
channel_name = models.CharField(max_length=1024)
|
||||||
icon_default = models.CharField(max_length=1024)
|
icon_default = models.CharField(max_length=1024)
|
||||||
icon_best = models.CharField(max_length=1024)
|
icon_best = models.CharField(max_length=1024)
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
@ -327,30 +251,42 @@ class Subscription(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def fill_from_playlist(self, info_playlist: YoutubePlaylistInfo):
|
def __repr__(self):
|
||||||
self.name = info_playlist.getTitle()
|
return f'subscription {self.id}, name="{self.name}", playlist_id="{self.playlist_id}"'
|
||||||
self.playlist_id = info_playlist.getId()
|
|
||||||
self.description = info_playlist.getDescription()
|
|
||||||
self.icon_default = info_playlist.getDefaultThumbnailUrl()
|
|
||||||
self.icon_best = info_playlist.getBestThumbnailUrl()
|
|
||||||
|
|
||||||
def copy_from_channel(self):
|
def fill_from_playlist(self, info_playlist: youtube.Playlist):
|
||||||
|
self.name = info_playlist.title
|
||||||
|
self.playlist_id = info_playlist.id
|
||||||
|
self.description = info_playlist.description
|
||||||
|
self.channel_id = info_playlist.channel_id
|
||||||
|
self.channel_name = info_playlist.channel_title
|
||||||
|
self.icon_default = youtube.default_thumbnail(info_playlist).url
|
||||||
|
self.icon_best = youtube.best_thumbnail(info_playlist).url
|
||||||
|
|
||||||
|
def copy_from_channel(self, info_channel: youtube.Channel):
|
||||||
# No point in storing info about the 'uploads from X' playlist
|
# No point in storing info about the 'uploads from X' playlist
|
||||||
self.name = self.channel.name
|
self.name = info_channel.title
|
||||||
self.playlist_id = self.channel.upload_playlist_id
|
self.playlist_id = info_channel.uploads_playlist.id
|
||||||
self.description = self.channel.description
|
self.description = info_channel.description
|
||||||
self.icon_default = self.channel.icon_default
|
self.channel_id = info_channel.id
|
||||||
self.icon_best = self.channel.icon_best
|
self.channel_name = info_channel.title
|
||||||
|
self.icon_default = youtube.default_thumbnail(info_channel).url
|
||||||
|
self.icon_best = youtube.best_thumbnail(info_channel).url
|
||||||
|
|
||||||
|
def fetch_from_url(self, url, yt_api: youtube.YoutubeAPI):
|
||||||
|
url_parsed = yt_api.parse_url(url)
|
||||||
|
if 'playlist' in url_parsed:
|
||||||
|
info_playlist = yt_api.playlist(url=url)
|
||||||
|
if info_playlist is None:
|
||||||
|
raise ValueError('Invalid playlist ID!')
|
||||||
|
|
||||||
def fetch_from_url(self, url, yt_api: YoutubeAPI):
|
|
||||||
url_type, url_id = yt_api.parse_channel_url(url)
|
|
||||||
if url_type == 'playlist_id':
|
|
||||||
info_playlist = yt_api.get_playlist_info(url_id)
|
|
||||||
self.channel = Channel.get_or_create('channel_id', info_playlist.getChannelId(), yt_api)
|
|
||||||
self.fill_from_playlist(info_playlist)
|
self.fill_from_playlist(info_playlist)
|
||||||
else:
|
else:
|
||||||
self.channel = Channel.get_or_create(url_type, url_id, yt_api)
|
info_channel = yt_api.channel(url=url)
|
||||||
self.copy_from_channel()
|
if info_channel is None:
|
||||||
|
raise ValueError('Cannot find channel!')
|
||||||
|
|
||||||
|
self.copy_from_channel(info_channel)
|
||||||
|
|
||||||
def delete_subscription(self, keep_downloaded_videos: bool):
|
def delete_subscription(self, keep_downloaded_videos: bool):
|
||||||
self.delete()
|
self.delete()
|
||||||
@ -383,6 +319,22 @@ class Video(models.Model):
|
|||||||
views = models.IntegerField(null=False, default=0)
|
views = models.IntegerField(null=False, default=0)
|
||||||
rating = models.FloatField(null=False, default=0.5)
|
rating = models.FloatField(null=False, default=0.5)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create(playlist_item: youtube.PlaylistItem, subscription: Subscription):
|
||||||
|
video = Video()
|
||||||
|
video.video_id = playlist_item.resource_video_id
|
||||||
|
video.name = playlist_item.title
|
||||||
|
video.description = playlist_item.description
|
||||||
|
video.watched = False
|
||||||
|
video.downloaded_path = None
|
||||||
|
video.subscription = subscription
|
||||||
|
video.playlist_index = playlist_item.position
|
||||||
|
video.publish_date = playlist_item.published_at
|
||||||
|
video.icon_default = youtube.default_thumbnail(playlist_item).url
|
||||||
|
video.icon_best = youtube.best_thumbnail(playlist_item).url
|
||||||
|
video.save()
|
||||||
|
return video
|
||||||
|
|
||||||
def mark_watched(self):
|
def mark_watched(self):
|
||||||
self.watched = True
|
self.watched = True
|
||||||
self.save()
|
self.save()
|
||||||
@ -428,3 +380,6 @@ class Video(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'video {self.id}, video_id="{self.video_id}"'
|
||||||
|
@ -2,12 +2,12 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
|
||||||
instance: BackgroundScheduler = None
|
scheduler: BackgroundScheduler = None
|
||||||
|
|
||||||
|
|
||||||
def initialize_scheduler():
|
def initialize_scheduler():
|
||||||
from .appconfig import settings
|
from .appconfig import settings
|
||||||
global instance
|
global scheduler
|
||||||
|
|
||||||
logger = logging.getLogger('scheduler')
|
logger = logging.getLogger('scheduler')
|
||||||
executors = {
|
executors = {
|
||||||
@ -17,8 +17,8 @@ def initialize_scheduler():
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
job_defaults = {
|
job_defaults = {
|
||||||
'misfire_grace_time': sys.maxsize
|
'misfire_grace_time': 60 * 60 * 24 * 365 # 1 year
|
||||||
}
|
}
|
||||||
|
|
||||||
instance = BackgroundScheduler(logger=logger, executors=executors, job_defaults=job_defaults)
|
scheduler = BackgroundScheduler(logger=logger, executors=executors, job_defaults=job_defaults)
|
||||||
instance.start()
|
scheduler.start()
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import itertools
|
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
|
|
||||||
def first_true(*args, default=False, pred=None):
|
|
||||||
"""Returns the first true value in the iterable.
|
|
||||||
|
|
||||||
If no true value is found, returns *default*
|
|
||||||
|
|
||||||
If *pred* is not None, returns the first item
|
|
||||||
for which pred(item) is true.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# first_true([a,b,c], x) --> a or b or c or x
|
|
||||||
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
|
|
||||||
return next(filter(pred, args), default)
|
|
||||||
|
|
||||||
|
|
||||||
def as_chunks(iterable: Iterable, chunk_size: int):
|
|
||||||
"""
|
|
||||||
Iterates an iterable in chunks of chunk_size elements.
|
|
||||||
:param iterable: An iterable containing items to iterate.
|
|
||||||
:param chunk_size: Chunk size
|
|
||||||
:return: Returns a generator which will yield chunks of size chunk_size
|
|
||||||
"""
|
|
||||||
|
|
||||||
it = iter(iterable)
|
|
||||||
while True:
|
|
||||||
chunk = tuple(itertools.islice(it, chunk_size))
|
|
||||||
if not chunk:
|
|
||||||
return
|
|
||||||
yield chunk
|
|
@ -1,285 +1,48 @@
|
|||||||
from googleapiclient.discovery import build
|
|
||||||
from googleapiclient.errors import Error as APIError
|
|
||||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import re
|
from external.pytaw.pytaw.youtube import YouTube, Channel, Playlist, PlaylistItem, Thumbnail, InvalidURL, Resource, Video
|
||||||
from YtManagerApp.utils.iterutils import as_chunks
|
from typing import Optional
|
||||||
|
|
||||||
API_SERVICE_NAME = 'youtube'
|
|
||||||
API_VERSION = 'v3'
|
|
||||||
|
|
||||||
YOUTUBE_LIST_LIMIT = 50
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeException(Exception):
|
class YoutubeAPI(YouTube):
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeInvalidURLException(YoutubeException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeChannelNotFoundException(YoutubeException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeUserNotFoundException(YoutubeException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistNotFoundException(YoutubeException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeVideoNotFoundException(YoutubeException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeChannelInfo(object):
|
|
||||||
def __init__(self, result_dict):
|
|
||||||
self.__id = result_dict['id']
|
|
||||||
self.__snippet = result_dict['snippet']
|
|
||||||
self.__contentDetails = result_dict['contentDetails']
|
|
||||||
|
|
||||||
def getId(self):
|
|
||||||
return self.__id
|
|
||||||
|
|
||||||
def getTitle(self):
|
|
||||||
return self.__snippet['title']
|
|
||||||
|
|
||||||
def getDescription(self):
|
|
||||||
return self.__snippet['description']
|
|
||||||
|
|
||||||
def getCustomUrl(self):
|
|
||||||
try:
|
|
||||||
return self.__snippet['customUrl']
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getDefaultThumbnailUrl(self):
|
|
||||||
return self.__snippet['thumbnails']['default']['url']
|
|
||||||
|
|
||||||
def getBestThumbnailUrl(self):
|
|
||||||
best_url = None
|
|
||||||
best_res = 0
|
|
||||||
for _, thumb in self.__snippet['thumbnails'].items():
|
|
||||||
res = thumb['width'] * thumb['height']
|
|
||||||
if res > best_res:
|
|
||||||
best_res = res
|
|
||||||
best_url = thumb['url']
|
|
||||||
return best_url
|
|
||||||
|
|
||||||
def getUploadsPlaylist(self):
|
|
||||||
return self.__contentDetails['relatedPlaylists']['uploads']
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistInfo(object):
|
|
||||||
def __init__(self, result_dict):
|
|
||||||
self.__id = result_dict['id']
|
|
||||||
self.__snippet = result_dict['snippet']
|
|
||||||
|
|
||||||
def getId(self):
|
|
||||||
return self.__id
|
|
||||||
|
|
||||||
def getChannelId(self):
|
|
||||||
return self.__snippet['channelId']
|
|
||||||
|
|
||||||
def getTitle(self):
|
|
||||||
return self.__snippet['title']
|
|
||||||
|
|
||||||
def getDescription(self):
|
|
||||||
return self.__snippet['description']
|
|
||||||
|
|
||||||
def getDefaultThumbnailUrl(self):
|
|
||||||
return self.__snippet['thumbnails']['default']['url']
|
|
||||||
|
|
||||||
def getBestThumbnailUrl(self):
|
|
||||||
best_url = None
|
|
||||||
best_res = 0
|
|
||||||
for _, thumb in self.__snippet['thumbnails'].items():
|
|
||||||
res = thumb['width'] * thumb['height']
|
|
||||||
if res > best_res:
|
|
||||||
best_res = res
|
|
||||||
best_url = thumb['url']
|
|
||||||
return best_url
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistItem(object):
|
|
||||||
def __init__(self, result_dict):
|
|
||||||
self.__snippet = result_dict['snippet']
|
|
||||||
|
|
||||||
def getVideoId(self):
|
|
||||||
return self.__snippet['resourceId']['videoId']
|
|
||||||
|
|
||||||
def getPublishDate(self):
|
|
||||||
return self.__snippet['publishedAt']
|
|
||||||
|
|
||||||
def getTitle(self):
|
|
||||||
return self.__snippet['title']
|
|
||||||
|
|
||||||
def getDescription(self):
|
|
||||||
return self.__snippet['description']
|
|
||||||
|
|
||||||
def getDefaultThumbnailUrl(self):
|
|
||||||
return self.__snippet['thumbnails']['default']['url']
|
|
||||||
|
|
||||||
def getBestThumbnailUrl(self):
|
|
||||||
best_url = None
|
|
||||||
best_res = 0
|
|
||||||
for _, thumb in self.__snippet['thumbnails'].items():
|
|
||||||
res = thumb['width'] * thumb['height']
|
|
||||||
if res > best_res:
|
|
||||||
best_res = res
|
|
||||||
best_url = thumb['url']
|
|
||||||
return best_url
|
|
||||||
|
|
||||||
def getPlaylistIndex(self):
|
|
||||||
return self.__snippet['position']
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeVideoStatistics(object):
|
|
||||||
def __init__(self, result_dict):
|
|
||||||
self.id = result_dict['id']
|
|
||||||
self.stats = result_dict['statistics']
|
|
||||||
|
|
||||||
def get_view_count(self):
|
|
||||||
return int(self.stats['viewCount'])
|
|
||||||
|
|
||||||
def get_like_count(self):
|
|
||||||
return int(self.stats['likeCount'])
|
|
||||||
|
|
||||||
def get_dislike_count(self):
|
|
||||||
return int(self.stats['dislikeCount'])
|
|
||||||
|
|
||||||
def get_favorite_count(self):
|
|
||||||
return int(self.stats['favoriteCount'])
|
|
||||||
|
|
||||||
def get_comment_count(self):
|
|
||||||
return int(self.stats['commentCount'])
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeAPI(object):
|
|
||||||
def __init__(self, service):
|
|
||||||
self.service = service
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def build_public() -> 'YoutubeAPI':
|
def build_public() -> 'YoutubeAPI':
|
||||||
service = build(API_SERVICE_NAME, API_VERSION, developerKey=settings.YOUTUBE_API_KEY)
|
return YoutubeAPI(key=settings.YOUTUBE_API_KEY)
|
||||||
return YoutubeAPI(service)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_channel_url(url):
|
|
||||||
"""
|
|
||||||
Parses given channel url, returns a tuple of the form (type, value), where type can be one of:
|
|
||||||
* channel_id
|
|
||||||
* channel_custom
|
|
||||||
* user
|
|
||||||
* playlist_id
|
|
||||||
:param url: URL to parse
|
|
||||||
:return: (type, value) tuple
|
|
||||||
"""
|
|
||||||
match = re.search(r'youtube\.com/.*[&?]list=([^?&/]+)', url)
|
|
||||||
if match:
|
|
||||||
return 'playlist_id', match.group(1)
|
|
||||||
|
|
||||||
match = re.search(r'youtube\.com/user/([^?&/]+)', url)
|
|
||||||
if match:
|
|
||||||
return 'user', match.group(1)
|
|
||||||
|
|
||||||
match = re.search(r'youtube\.com/channel/([^?&/]+)', url)
|
|
||||||
if match:
|
|
||||||
return 'channel_id', match.group(1)
|
|
||||||
|
|
||||||
match = re.search(r'youtube\.com/(?:c/)?([^?&/]+)', url)
|
|
||||||
if match:
|
|
||||||
return 'channel_custom', match.group(1)
|
|
||||||
|
|
||||||
raise YoutubeInvalidURLException('Unrecognized URL format!')
|
|
||||||
|
|
||||||
def get_playlist_info(self, list_id) -> YoutubePlaylistInfo:
|
|
||||||
result = self.service.playlists()\
|
|
||||||
.list(part='snippet', id=list_id)\
|
|
||||||
.execute()
|
|
||||||
|
|
||||||
if len(result['items']) <= 0:
|
|
||||||
raise YoutubePlaylistNotFoundException("Invalid playlist ID.")
|
|
||||||
|
|
||||||
return YoutubePlaylistInfo(result['items'][0])
|
|
||||||
|
|
||||||
def get_channel_info_by_username(self, user) -> YoutubeChannelInfo:
|
|
||||||
result = self.service.channels()\
|
|
||||||
.list(part='snippet,contentDetails', forUsername=user)\
|
|
||||||
.execute()
|
|
||||||
|
|
||||||
if len(result['items']) <= 0:
|
|
||||||
raise YoutubeUserNotFoundException('Invalid user.')
|
|
||||||
|
|
||||||
return YoutubeChannelInfo(result['items'][0])
|
|
||||||
|
|
||||||
def get_channel_info(self, channel_id) -> YoutubeChannelInfo:
|
|
||||||
result = self.service.channels()\
|
|
||||||
.list(part='snippet,contentDetails', id=channel_id)\
|
|
||||||
.execute()
|
|
||||||
|
|
||||||
if len(result['items']) <= 0:
|
|
||||||
raise YoutubeChannelNotFoundException('Invalid channel ID.')
|
|
||||||
|
|
||||||
return YoutubeChannelInfo(result['items'][0])
|
|
||||||
|
|
||||||
def search_channel(self, custom) -> str:
|
|
||||||
result = self.service.search()\
|
|
||||||
.list(part='id', q=custom, type='channel')\
|
|
||||||
.execute()
|
|
||||||
|
|
||||||
if len(result['items']) <= 0:
|
|
||||||
raise YoutubeChannelNotFoundException('Could not find channel!')
|
|
||||||
|
|
||||||
channel_result = result['items'][0]
|
|
||||||
return channel_result['id']['channelId']
|
|
||||||
|
|
||||||
def list_playlist_videos(self, playlist_id):
|
|
||||||
kwargs = {
|
|
||||||
"part": "snippet",
|
|
||||||
"maxResults": 50,
|
|
||||||
"playlistId": playlist_id
|
|
||||||
}
|
|
||||||
last_page = False
|
|
||||||
|
|
||||||
while not last_page:
|
|
||||||
result = self.service.playlistItems()\
|
|
||||||
.list(**kwargs)\
|
|
||||||
.execute()
|
|
||||||
|
|
||||||
for item in result['items']:
|
|
||||||
yield YoutubePlaylistItem(item)
|
|
||||||
|
|
||||||
if 'nextPageToken' in result:
|
|
||||||
kwargs['pageToken'] = result['nextPageToken']
|
|
||||||
else:
|
|
||||||
last_page = True
|
|
||||||
|
|
||||||
def get_single_video_stats(self, video_id) -> YoutubeVideoStatistics:
|
|
||||||
result = list(self.get_video_stats([video_id]))
|
|
||||||
if len(result) < 1:
|
|
||||||
raise YoutubeVideoNotFoundException('Could not find video with id ' + video_id + '!')
|
|
||||||
return result[0]
|
|
||||||
|
|
||||||
def get_video_stats(self, video_id_list):
|
|
||||||
for chunk in as_chunks(video_id_list, YOUTUBE_LIST_LIMIT):
|
|
||||||
kwargs = {
|
|
||||||
"part": "statistics",
|
|
||||||
"maxResults": YOUTUBE_LIST_LIMIT,
|
|
||||||
"id": ','.join(chunk)
|
|
||||||
}
|
|
||||||
result = self.service.videos()\
|
|
||||||
.list(**kwargs)\
|
|
||||||
.execute()
|
|
||||||
|
|
||||||
for item in result['items']:
|
|
||||||
yield YoutubeVideoStatistics(item)
|
|
||||||
|
|
||||||
# @staticmethod
|
# @staticmethod
|
||||||
# def build_oauth() -> 'YoutubeAPI':
|
# def build_oauth() -> 'YoutubeAPI':
|
||||||
# flow =
|
# flow =
|
||||||
# credentials =
|
# credentials =
|
||||||
# service = build(API_SERVICE_NAME, API_VERSION, credentials)
|
# service = build(API_SERVICE_NAME, API_VERSION, credentials)
|
||||||
|
|
||||||
|
|
||||||
|
def default_thumbnail(resource: Resource) -> Optional[Thumbnail]:
|
||||||
|
"""
|
||||||
|
Gets the default thumbnail for a resource.
|
||||||
|
Searches in the list of thumbnails for one with the label 'default', or takes the first one.
|
||||||
|
:param resource:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
thumbs = getattr(resource, 'thumbnails', None)
|
||||||
|
|
||||||
|
if thumbs is None or len(thumbs) <= 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return next(
|
||||||
|
(i for i in thumbs if i.id == 'default'),
|
||||||
|
thumbs[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def best_thumbnail(resource: Resource) -> Optional[Thumbnail]:
|
||||||
|
"""
|
||||||
|
Gets the best thumbnail available for a resource.
|
||||||
|
:param resource:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
thumbs = getattr(resource, 'thumbnails', None)
|
||||||
|
|
||||||
|
if thumbs is None or len(thumbs) <= 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return max(thumbs, key=lambda t: t.width * t.height)
|
@ -1,4 +1,4 @@
|
|||||||
from crispy_forms.helper import FormHelperpython3
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Layout, Field, HTML
|
from crispy_forms.layout import Layout, Field, HTML
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
@ -175,7 +175,8 @@ class SubscriptionFolderForm(forms.ModelForm):
|
|||||||
args_id.append(~Q(id=self.instance.id))
|
args_id.append(~Q(id=self.instance.id))
|
||||||
|
|
||||||
if SubscriptionFolder.objects.filter(parent=parent, name__iexact=name, *args_id).count() > 0:
|
if SubscriptionFolder.objects.filter(parent=parent, name__iexact=name, *args_id).count() > 0:
|
||||||
raise forms.ValidationError('A folder with the same name already exists in the given parent directory!', code='already_exists')
|
raise forms.ValidationError(
|
||||||
|
'A folder with the same name already exists in the given parent directory!', code='already_exists')
|
||||||
|
|
||||||
# Check for cycles
|
# Check for cycles
|
||||||
if self.instance is not None:
|
if self.instance is not None:
|
||||||
@ -238,6 +239,7 @@ class CreateSubscriptionForm(forms.ModelForm):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
self.yt_api = youtube.YoutubeAPI.build_public()
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_tag = False
|
self.helper.form_tag = False
|
||||||
self.helper.layout = Layout(
|
self.helper.layout = Layout(
|
||||||
@ -252,11 +254,18 @@ class CreateSubscriptionForm(forms.ModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def clean_playlist_url(self):
|
def clean_playlist_url(self):
|
||||||
playlist_url = self.cleaned_data['playlist_url']
|
playlist_url: str = self.cleaned_data['playlist_url']
|
||||||
try:
|
try:
|
||||||
youtube.YoutubeAPI.parse_channel_url(playlist_url)
|
parsed_url = self.yt_api.parse_url(playlist_url)
|
||||||
except youtube.YoutubeInvalidURLException:
|
except youtube.InvalidURL as e:
|
||||||
raise forms.ValidationError('Invalid playlist/channel URL, or not in a recognized format.')
|
raise forms.ValidationError(str(e))
|
||||||
|
|
||||||
|
is_playlist = 'playlist' in parsed_url
|
||||||
|
is_channel = parsed_url['type'] in ('channel', 'user', 'channel_custom')
|
||||||
|
|
||||||
|
if not is_channel and not is_playlist:
|
||||||
|
raise forms.ValidationError('The given URL must link to a channel or a playlist!')
|
||||||
|
|
||||||
return playlist_url
|
return playlist_url
|
||||||
|
|
||||||
|
|
||||||
@ -269,21 +278,22 @@ class CreateSubscriptionModal(LoginRequiredMixin, ModalMixin, CreateView):
|
|||||||
api = youtube.YoutubeAPI.build_public()
|
api = youtube.YoutubeAPI.build_public()
|
||||||
try:
|
try:
|
||||||
form.instance.fetch_from_url(form.cleaned_data['playlist_url'], api)
|
form.instance.fetch_from_url(form.cleaned_data['playlist_url'], api)
|
||||||
except youtube.YoutubeChannelNotFoundException:
|
except youtube.InvalidURL as e:
|
||||||
return self.modal_response(
|
return self.modal_response(form, False, str(e))
|
||||||
form, False, 'Could not find a channel based on the given URL. Please verify that the URL is correct.')
|
except ValueError as e:
|
||||||
except youtube.YoutubeUserNotFoundException:
|
return self.modal_response(form, False, str(e))
|
||||||
return self.modal_response(
|
# except youtube.YoutubeUserNotFoundException:
|
||||||
form, False, 'Could not find an user based on the given URL. Please verify that the URL is correct.')
|
# return self.modal_response(
|
||||||
except youtube.YoutubePlaylistNotFoundException:
|
# form, False, 'Could not find an user based on the given URL. Please verify that the URL is correct.')
|
||||||
return self.modal_response(
|
# except youtube.YoutubePlaylistNotFoundException:
|
||||||
form, False, 'Could not find a playlist based on the given URL. Please verify that the URL is correct.')
|
# return self.modal_response(
|
||||||
except youtube.YoutubeException as e:
|
# form, False, 'Could not find a playlist based on the given URL. Please verify that the URL is correct.')
|
||||||
return self.modal_response(
|
# except youtube.YoutubeException as e:
|
||||||
form, False, str(e))
|
# return self.modal_response(
|
||||||
except youtube.APIError as e:
|
# form, False, str(e))
|
||||||
return self.modal_response(
|
# except youtube.APIError as e:
|
||||||
form, False, 'An error occurred while communicating with the YouTube API: ' + str(e))
|
# return self.modal_response(
|
||||||
|
# form, False, 'An error occurred while communicating with the YouTube API: ' + str(e))
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ LogLevel=DEBUG
|
|||||||
;DownloadOrder=playlist
|
;DownloadOrder=playlist
|
||||||
|
|
||||||
; Path where downloaded videos are stored
|
; Path where downloaded videos are stored
|
||||||
DownloadPath=D:\\Dev\\youtube-channel-manager\\temp\\download
|
DownloadPath=temp/download
|
||||||
|
|
||||||
; A pattern which describes how downloaded files are organized. Extensions are automatically appended.
|
; A pattern which describes how downloaded files are organized. Extensions are automatically appended.
|
||||||
; Supported fields: channel, channel_id, playlist, playlist_id, playlist_index, title, id
|
; Supported fields: channel, channel_id, playlist, playlist_id, playlist_index, title, id
|
||||||
|
0
external/__init__.py
vendored
Normal file
0
external/__init__.py
vendored
Normal file
19
external/pytaw/.gitignore
vendored
Normal file
19
external/pytaw/.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
*.bak
|
||||||
|
*.egg
|
||||||
|
*.egg-info/
|
||||||
|
*.eggs/
|
||||||
|
*.pyproj
|
||||||
|
*.sln
|
||||||
|
*.vs/
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
.cache/
|
||||||
|
.coverage
|
||||||
|
.idea/
|
||||||
|
.tox/
|
||||||
|
_build/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
__pycache__/
|
||||||
|
*.ini
|
3
external/pytaw/.pytaw.conf
vendored
Normal file
3
external/pytaw/.pytaw.conf
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
; by default pytaw will look for this file (".pytaw.conf") in the user's home directory
|
||||||
|
[youtube]
|
||||||
|
developer_key = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
29
external/pytaw/README.md
vendored
Normal file
29
external/pytaw/README.md
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# PYTAW: Python YouTube API Wrapper
|
||||||
|
|
||||||
|
###Note
|
||||||
|
This library is copied from [https://github.com/chibicitiberiu/pytaw/tree/improvements](https://github.com/chibicitiberiu/pytaw/tree/improvements).
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> from pytaw import YouTube
|
||||||
|
>>> youtube = YouTube(key='your_api_key')
|
||||||
|
>>> video = youtube.video('4vuW6tQ0218')
|
||||||
|
>>> video.title
|
||||||
|
'Monty Python - Dead Parrot'
|
||||||
|
>>> video.published_at
|
||||||
|
datetime.datetime(2007, 2, 14, 13, 55, 51, tzinfo=tzutc())
|
||||||
|
>>> channel = video.channel
|
||||||
|
>>> channel.title
|
||||||
|
'Chadner'
|
||||||
|
>>> search = youtube.search(q='monty python')
|
||||||
|
>>> search[0]
|
||||||
|
<Channel UCGm3CO6LPcN-Y7HIuyE0Rew "Monty Python">
|
||||||
|
>>> for r in search[:5]:
|
||||||
|
... print(r)
|
||||||
|
...
|
||||||
|
Monty Python
|
||||||
|
Chemist Sketch - Monty Python's Flying Circus
|
||||||
|
A Selection of Sketches from "Monty Python's Flying Circus" - #4
|
||||||
|
Monty Python - Dead Parrot
|
||||||
|
Monty Python And the holy grail
|
||||||
|
```
|
0
external/pytaw/__init__.py
vendored
Normal file
0
external/pytaw/__init__.py
vendored
Normal file
20
external/pytaw/docs/Makefile
vendored
Normal file
20
external/pytaw/docs/Makefile
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
SPHINXPROJ = pytaw
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
169
external/pytaw/docs/conf.py
vendored
Normal file
169
external/pytaw/docs/conf.py
vendored
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# pytaw documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Mon Nov 27 19:26:35 2017.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its
|
||||||
|
# containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
# import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#
|
||||||
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = ['sphinx.ext.autodoc']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix(es) of source filenames.
|
||||||
|
# You can specify multiple suffix as a list of string:
|
||||||
|
#
|
||||||
|
# source_suffix = ['.rst', '.md']
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = 'pytaw'
|
||||||
|
copyright = '2017, 6000hulls'
|
||||||
|
author = '6000hulls'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.1'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.1'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#
|
||||||
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
|
# Usually you set "language" from the command line for these cases.
|
||||||
|
language = None
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
|
todo_include_todos = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
html_theme = 'sphinx_rtd_theme'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#
|
||||||
|
# html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
# Custom sidebar templates, must be a dictionary that maps document names
|
||||||
|
# to template names.
|
||||||
|
#
|
||||||
|
# This is required for the alabaster theme
|
||||||
|
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||||
|
html_sidebars = {
|
||||||
|
'**': [
|
||||||
|
'relations.html', # needs 'show_related': True theme option to display
|
||||||
|
'searchbox.html',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTMLHelp output ------------------------------------------
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'pytawdoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#
|
||||||
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#
|
||||||
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#
|
||||||
|
# 'preamble': '',
|
||||||
|
|
||||||
|
# Latex figure (float) alignment
|
||||||
|
#
|
||||||
|
# 'figure_align': 'htbp',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title,
|
||||||
|
# author, documentclass [howto, manual, or own class]).
|
||||||
|
latex_documents = [
|
||||||
|
(master_doc, 'pytaw.tex', 'pytaw Documentation',
|
||||||
|
'6000hulls', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output ---------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
(master_doc, 'pytaw', 'pytaw Documentation',
|
||||||
|
[author], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output -------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
(master_doc, 'pytaw', 'pytaw Documentation',
|
||||||
|
author, 'pytaw', 'One line description of project.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
18
external/pytaw/docs/index.rst
vendored
Normal file
18
external/pytaw/docs/index.rst
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
PYTAW: Python YouTube API Wrapper
|
||||||
|
=================================
|
||||||
|
|
||||||
|
It's a wrapper for the YouTube python API. Written in python.
|
||||||
|
|
||||||
|
.. automodule:: pytaw.youtube
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
36
external/pytaw/docs/make.bat
vendored
Normal file
36
external/pytaw/docs/make.bat
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=.
|
||||||
|
set BUILDDIR=_build
|
||||||
|
set SPHINXPROJ=pytaw
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.http://sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
13
external/pytaw/main_test.py
vendored
Normal file
13
external/pytaw/main_test.py
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import pytaw
|
||||||
|
|
||||||
|
yt = pytaw.YouTube(key='AIzaSyBabzE4Bup77WexdLMa9rN9z-wJidEfNX8')
|
||||||
|
c = yt.channel('UCmmPgObSUPw1HL2lq6H4ffA')
|
||||||
|
|
||||||
|
uploads_playlist = c.uploads_playlist
|
||||||
|
print(repr(uploads_playlist))
|
||||||
|
|
||||||
|
uploads_list = list(uploads_playlist.items)
|
||||||
|
for item in uploads_list:
|
||||||
|
print(item.position, '...', repr(item), ' .... ', repr(item.video))
|
||||||
|
print(item.thumbnails)
|
||||||
|
break
|
1
external/pytaw/pytaw/__init__.py
vendored
Normal file
1
external/pytaw/pytaw/__init__.py
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .youtube import YouTube
|
92
external/pytaw/pytaw/utils.py
vendored
Normal file
92
external/pytaw/pytaw/utils.py
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import re
|
||||||
|
import urllib.parse
|
||||||
|
import typing
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
import dateutil.parser
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
def string_to_datetime(string):
|
||||||
|
if string is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return dateutil.parser.parse(string)
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_to_string(dt):
|
||||||
|
if dt is None:
|
||||||
|
return None
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
dt = dt.astimezone(timezone.utc)
|
||||||
|
return dt.isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
def youtube_url_to_id(url):
|
||||||
|
"""Extract video id from a youtube url.
|
||||||
|
|
||||||
|
If parsing fails, try regex. If that fails, return None.
|
||||||
|
|
||||||
|
The regex is from somewhere in this thread, I think:
|
||||||
|
https://stackoverflow.com/questions/3452546/how-do-i-get-the-youtube-video-id-from-a-url
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = urllib.parse.unquote(url)
|
||||||
|
url_data = urllib.parse.urlparse(url)
|
||||||
|
query = urllib.parse.parse_qs(url_data.query)
|
||||||
|
try:
|
||||||
|
# parse the url for a video query
|
||||||
|
return query["v"][0]
|
||||||
|
except KeyError:
|
||||||
|
# use regex to try and extract id
|
||||||
|
match = re.search(
|
||||||
|
r"((?<=(v|V)/)|(?<=be/)|(?<=(\?|\&)v=)|(?<=embed/))([\w-]+)",
|
||||||
|
url,
|
||||||
|
)
|
||||||
|
if match:
|
||||||
|
return match.group()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def youtube_duration_to_seconds(value):
|
||||||
|
"""Convert youtube (ISO 8601) duration to seconds.
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/ISO_8601#Durations
|
||||||
|
https://regex101.com/r/ALmmSS/1
|
||||||
|
|
||||||
|
"""
|
||||||
|
iso8601 = r"P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?T?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
match = re.match(iso8601, value)
|
||||||
|
if match is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
group_names = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']
|
||||||
|
d = dict()
|
||||||
|
for name, group in zip(group_names, match.groups(default=0)):
|
||||||
|
d[name] = int(group)
|
||||||
|
|
||||||
|
return int(
|
||||||
|
d['years']*365*24*60*60 +
|
||||||
|
d['months']*30*24*60*60 +
|
||||||
|
d['weeks']*7*24*60*60 +
|
||||||
|
d['days']*24*60*60 +
|
||||||
|
d['hours']*60*60 +
|
||||||
|
d['minutes']*60 +
|
||||||
|
d['seconds']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def iterate_chunks(iterable: typing.Iterable, chunk_size: int):
|
||||||
|
"""
|
||||||
|
Iterates an iterable in chunks of chunk_size elements.
|
||||||
|
:param iterable: An iterable containing items to iterate.
|
||||||
|
:param chunk_size: Chunk size
|
||||||
|
:return: Returns a generator which will yield chunks of size chunk_size
|
||||||
|
"""
|
||||||
|
|
||||||
|
it = iter(iterable)
|
||||||
|
while True:
|
||||||
|
chunk = tuple(itertools.islice(it, chunk_size))
|
||||||
|
if not chunk:
|
||||||
|
return
|
||||||
|
yield chunk
|
1055
external/pytaw/pytaw/youtube.py
vendored
Normal file
1055
external/pytaw/pytaw/youtube.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
external/pytaw/setup.py
vendored
Normal file
12
external/pytaw/setup.py
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='pytaw',
|
||||||
|
version='0.0.1',
|
||||||
|
packages=['pytaw'],
|
||||||
|
url='https://github.com/6000hulls/pytaw',
|
||||||
|
license='',
|
||||||
|
author='6000hulls',
|
||||||
|
author_email='6000hulls@gmail.com',
|
||||||
|
description='PYTAW: Python YouTube API Wrapper'
|
||||||
|
)
|
0
external/pytaw/tests/__init__.py
vendored
Normal file
0
external/pytaw/tests/__init__.py
vendored
Normal file
165
external/pytaw/tests/test_pytaw.py
vendored
Normal file
165
external/pytaw/tests/test_pytaw.py
vendored
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import pytest
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import collections
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from googleapiclient.errors import HttpError
|
||||||
|
|
||||||
|
from pytaw import YouTube
|
||||||
|
from pytaw.youtube import Resource, Video, AttributeDef
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(stream=sys.stdout) # show log output when run with pytest -s
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def youtube():
|
||||||
|
"""A YouTube instance initialised with a developer key loaded from config.ini"""
|
||||||
|
return YouTube()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def video(youtube):
|
||||||
|
"""A Video instance for the classic video 'Me at the zoo'"""
|
||||||
|
return youtube.video('jNQXAC9IVRw')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def channel(youtube):
|
||||||
|
"""A Channel instance for the 'YouTube Help' channel"""
|
||||||
|
return youtube.channel('UCMDQxm7cUx3yXkfeHa5zJIQ')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def search(youtube):
|
||||||
|
"""A ListResponse instance corresponding to a search for the query 'python'"""
|
||||||
|
return youtube.search()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def video_search(youtube):
|
||||||
|
"""A ListResponse instance corresponding to a video search for the query 'python'"""
|
||||||
|
return youtube.search(q='python', type='video')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def video_search_array(youtube):
|
||||||
|
"""An array of video searches with a wide range of results (zero to millions)."""
|
||||||
|
one_minute_ago = datetime.utcnow() - timedelta(minutes=1)
|
||||||
|
five_minutes_ago = datetime.utcnow() - timedelta(minutes=5)
|
||||||
|
return [
|
||||||
|
#
|
||||||
|
# no results
|
||||||
|
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2000, 1, 1)),
|
||||||
|
#
|
||||||
|
# less than 100 results
|
||||||
|
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2005, 7, 1)),
|
||||||
|
#
|
||||||
|
# over 100 results
|
||||||
|
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2006, 1, 1)),
|
||||||
|
#
|
||||||
|
# variable number of results (hundreds or thousands...?)
|
||||||
|
youtube.search(q='minecraft', type='video', publishedAfter=one_minute_ago),
|
||||||
|
youtube.search(q='minecraft', type='video', publishedAfter=five_minutes_ago),
|
||||||
|
#
|
||||||
|
# over a million results
|
||||||
|
youtube.search(q='minecraft', type='video'),
|
||||||
|
youtube.search(q='minecraft'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestResource:
|
||||||
|
|
||||||
|
def test_equality(self, search):
|
||||||
|
a = search[0]
|
||||||
|
b = search[0]
|
||||||
|
c = search[1]
|
||||||
|
assert a == b
|
||||||
|
assert a != c
|
||||||
|
|
||||||
|
def test_unknown_attribute(self, video):
|
||||||
|
with pytest.raises(AttributeError):
|
||||||
|
_ = video.attribute_name_which_definitely_will_never_exist
|
||||||
|
|
||||||
|
def test_unknown_part_in_attributedef(self, video):
|
||||||
|
video.ATTRIBUTE_DEFS['x'] = AttributeDef('nonexistant_part', 'x')
|
||||||
|
with pytest.raises(HttpError):
|
||||||
|
_ = video.x
|
||||||
|
|
||||||
|
def test_unknown_attribute_name_in_attributedef(self, video):
|
||||||
|
video.ATTRIBUTE_DEFS['x'] = AttributeDef('snippet', 'nonexistant_attribute')
|
||||||
|
assert video.x is None
|
||||||
|
|
||||||
|
|
||||||
|
class TestVideo:
|
||||||
|
|
||||||
|
def test_bad_video_id(self, youtube):
|
||||||
|
video = youtube.video('not_a_valid_youtube_video_id')
|
||||||
|
assert video is None
|
||||||
|
|
||||||
|
def test_title(self, video):
|
||||||
|
assert video.title == "Me at the zoo"
|
||||||
|
|
||||||
|
def test_published_at(self, video):
|
||||||
|
assert video.published_at.isoformat() == '2005-04-24T03:31:52+00:00'
|
||||||
|
|
||||||
|
def test_n_views(self, video):
|
||||||
|
assert video.n_views > int(40e6)
|
||||||
|
|
||||||
|
def test_tags(self, video):
|
||||||
|
assert video.tags == ['jawed', 'karim', 'elephant', 'zoo', 'youtube', 'first', 'video']
|
||||||
|
|
||||||
|
def test_duration(self, video):
|
||||||
|
assert video.duration.total_seconds() == 19
|
||||||
|
|
||||||
|
|
||||||
|
class TestChannel:
|
||||||
|
|
||||||
|
def test_title(self, channel):
|
||||||
|
assert channel.title == "YouTube Help"
|
||||||
|
|
||||||
|
|
||||||
|
class TestSearch:
|
||||||
|
|
||||||
|
def test_video_search_returns_a_video(self, video_search):
|
||||||
|
assert isinstance(video_search[0], Video)
|
||||||
|
|
||||||
|
def test_video_search_has_many_results(self, video_search):
|
||||||
|
# make video_search unlazy (populate pageInfo attributes)
|
||||||
|
_ = video_search[0]
|
||||||
|
assert video_search.total_results > 10000
|
||||||
|
|
||||||
|
def test_search_iteration(self, search):
|
||||||
|
"""Simply iterate over a search, creating all resources, to check for exceptions."""
|
||||||
|
for resource in search:
|
||||||
|
log.debug(resource)
|
||||||
|
|
||||||
|
|
||||||
|
class TestListResponse:
|
||||||
|
|
||||||
|
def test_if_iterable(self, search):
|
||||||
|
assert isinstance(search, collections.Iterator)
|
||||||
|
|
||||||
|
def test_integer_indexing(self, search):
|
||||||
|
assert isinstance(search[0], Resource)
|
||||||
|
|
||||||
|
def test_slice_indexing(self, search):
|
||||||
|
assert isinstance(search[1:3], list)
|
||||||
|
|
||||||
|
def test_full_listing_iteration(self, video_search_array):
|
||||||
|
"""Iterate over all search results to check no exceptions are raised when paging etc.
|
||||||
|
|
||||||
|
Even if millions of results are found, the API will never return more than 500 (by
|
||||||
|
design), so we're okay to just bang right through the search results generator for the
|
||||||
|
whole array of video searches.
|
||||||
|
|
||||||
|
"""
|
||||||
|
for i, search in enumerate(video_search_array):
|
||||||
|
c = 0
|
||||||
|
for _ in search:
|
||||||
|
c += 1
|
||||||
|
|
||||||
|
log.debug(f"checked first {c} results (search #{i})")
|
@ -10,4 +10,5 @@ A self-hosted tool which manages your YouTube subscriptions, and downloads files
|
|||||||
* youtube-dl: `$ pip3 install youtube-dl`
|
* youtube-dl: `$ pip3 install youtube-dl`
|
||||||
* google-api-python-client: `$ pip3 install google-api-python-client`
|
* google-api-python-client: `$ pip3 install google-api-python-client`
|
||||||
* google_auth_oauthlib: `$ pip3 install google_auth_oauthlib`
|
* google_auth_oauthlib: `$ pip3 install google_auth_oauthlib`
|
||||||
* apscheduler (v3.5+): `$ pip3 install apscheduler`
|
* apscheduler (v3.5+): `$ pip3 install apscheduler`
|
||||||
|
* (recommended) oauth2client: `$ pip3 install oauth2client`
|
Loading…
x
Reference in New Issue
Block a user